Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
Merge 7dd774a into 8480bfe
Browse files Browse the repository at this point in the history
  • Loading branch information
dstrop committed Jan 2, 2023
2 parents 8480bfe + 7dd774a commit 66ea0c5
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 220 deletions.
22 changes: 12 additions & 10 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,26 @@
],
"require": {
"php": ">=8 <8.3",
"nette/di": "^3.0.7",
"nette/utils": "^3.0",
"nette/application": "^3.0.7",
"nette/di": "^3.0.7",
"nette/http": "^3.0",
"nette/schema": "^1.2",
"spiral/roadrunner-http": "^2.0",
"nyholm/psr7": "^1.4",
"psr/http-server-middleware": "^1.0"
"nette/utils": "^3.0",
"psr/http-server-middleware": "^1.0",
"spiral/roadrunner-http": "^2.0"
},
"require-dev": {
"nette/bootstrap": "^3.0",
"squizlabs/php_codesniffer": "^3.6",
"nette/tester": "^2.4",
"phpstan/phpstan-nette": "^1.0",
"vimeo/psalm": "^4.19",
"squizlabs/php_codesniffer": "^3.6",
"tracy/tracy": "^2.9",
"nette/tester": "^2.4"
"vimeo/psalm": "^5.3"
},
"suggest": {
"tracy/tracy": "To set tracyHook and get Tracy blueScreen on exceptions"
"tracy/tracy": "To set tracyHook and get Tracy blueScreen on exceptions",
"guzzlehttp/psr7": "A implementation of PSR-7",
"nyholm/psr7": "A implementation of PSR-7"
},
"scripts": {
"phpcs": "./vendor/bin/phpcs -p -n --standard=ruleset.xml",
Expand All @@ -59,6 +60,7 @@
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true
}
},
"sort-packages": true
}
}
18 changes: 14 additions & 4 deletions config/roadrunner.neon
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
extensions:
roadrunner: Mallgroup\RoadRunner\DI\Extension

services:
# register any Psr7 factory implementation
- GuzzleHttp\Psr7\HttpFactory

roadrunner:
showExceptions: true
middlewares:
- TracyMiddleware
- SessionMiddleware
- PsrApplication
# safety net middleware trying to prevent accidental stdout leak
# RoadRunner in pipe mode uses stdin/stdout for comunications
- Mallgroup\RoadRunner\Middlewares\OutputBufferMiddleware()
# safety net middleware trying to prevent accidental application exception
# it will catch, log and respond with generic 500 message or a tracy bluescreen
- Mallgroup\RoadRunner\Middlewares\TryCatchMiddleware(%debugMode%)
# Nette http/sessions extension companion middleware
# this middleware will start, close and clean sessions between requests preventing leaks
# and it will write session cookie into the response
- Mallgroup\RoadRunner\Middlewares\SessionMiddleware()
90 changes: 49 additions & 41 deletions src/DI/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@
use Mallgroup\RoadRunner\Http\Request;
use Mallgroup\RoadRunner\Http\RequestFactory;
use Mallgroup\RoadRunner\Http\Response;
use Mallgroup\RoadRunner\Middlewares\NetteApplicationMiddleware;
use Mallgroup\RoadRunner\Middlewares\NetteInitializeMiddleware;
use Mallgroup\RoadRunner\NetteApplicationHandler;
use Mallgroup\RoadRunner\PsrChain;
use Mallgroup\RoadRunner\RoadRunner;
use Nette;
use Nette\DI\ContainerBuilder;
use Nette\DI\Definitions\ServiceDefinition;
use Nette\Schema\Expect;
use Nette\Schema\Schema;
use Nyholm\Psr7\Factory\Psr17Factory;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\WorkerInterface;
use Tracy;

/**
* @property-read \stdClass $config
* @property \stdClass $config
* @psalm-property object{errorPresenter:string, catchExceptions:bool, middlewares:list<mixed>}&\stdClass $config
*/
class Extension extends Nette\DI\CompilerExtension
{
Expand Down Expand Up @@ -100,7 +104,7 @@ public static function initializeBlueScreenPanel(
Tracy\BlueScreen $blueScreen,
Nette\Http\IRequest $httpRequest,
Nette\Http\IResponse $httpResponse,
NetteApplicationMiddleware $application,
NetteApplicationHandler $application,
): void {
$blueScreen->addPanel(
function (?\Throwable $e) use ($application, $blueScreen, $httpResponse, $httpRequest): ?array {
Expand All @@ -120,14 +124,16 @@ function (?\Throwable $e) use ($application, $blueScreen, $httpResponse, $httpRe
private function setupTagsEvents(ContainerBuilder $builder)
{
foreach (self::RR_FLUSHABLE_SERVICES as $service) {
$builder->getDefinition($service)->addTag(self::RR_FLUSH);
if ($builder->hasDefinition($service)) {
$builder->getDefinition($service)->addTag(self::RR_FLUSH);
}
}
}

protected function createEventsDefinition(ContainerBuilder $builder): ServiceDefinition
{
return $this->createServiceDefinition($builder, 'events')
->setFactory(Events::class);
->setFactory(Events::class);
}

protected function replaceNetteHttpStuff(ContainerBuilder $builder)
Expand All @@ -149,25 +155,21 @@ protected function replaceNetteHttpStuff(ContainerBuilder $builder)
protected function createPsrStuff(ContainerBuilder $builder)
{
$this->createServiceDefinition($builder, 'worker')
->setFactory('Spiral\RoadRunner\Worker::create')
->setType(WorkerInterface::class)
->setAutowired(false);

$this->createServiceDefinition($builder, 'psr17factory')
->setFactory(Psr17Factory::class)
->setAutowired(false);
->setFactory('Spiral\RoadRunner\Worker::create')
->setType(WorkerInterface::class)
->setAutowired(false);

$this->createServiceDefinition($builder, 'psrWorker')
->setFactory(
PSR7Worker::class,
[
'@' . $this->prefix('worker'),
'@' . $this->prefix('psr17factory'),
'@' . $this->prefix('psr17factory'),
'@' . $this->prefix('psr17factory'),
]
)
->setAutowired(false);
->setFactory(
PSR7Worker::class,
[
'@' . $this->prefix('worker'),
'@' . ServerRequestFactoryInterface::class,
'@' . StreamFactoryInterface::class,
'@' . UploadedFileFactoryInterface::class,
]
)
->setAutowired(false);
}

private function createServiceDefinition(ContainerBuilder $builder, string $name): ServiceDefinition
Expand All @@ -178,11 +180,12 @@ private function createServiceDefinition(ContainerBuilder $builder, string $name
protected function createApplication(ContainerBuilder $builder)
{
$this->createServiceDefinition($builder, 'application')
->setFactory(NetteApplicationMiddleware::class)
->addSetup('$catchExceptions', [$this->config->catchExceptions])
->addSetup('$errorPresenter', [$this->config->errorPresenter])
->setFactory(NetteApplicationHandler::class)
->setAutowired(NetteApplicationHandler::class)
->addSetup('$catchExceptions', [$this->config->catchExceptions])
->addSetup('$errorPresenter', [$this->config->errorPresenter])
->addSetup('$onResponse[] = ?', [
(string) (new Nette\PhpGenerator\Literal(
(string)(new Nette\PhpGenerator\Literal(
'function() { Nette\Http\Helpers::initCookie($this->getService(?), $this->getService(?));};',
[$this->prefix('response'), $this->prefix('request')]
))
Expand All @@ -192,23 +195,28 @@ protected function createApplication(ContainerBuilder $builder)
protected function createRoadRunner(ContainerBuilder $builder)
{
$this->createServiceDefinition($builder, 'roadrunner')
->setFactory(
RoadRunner::class,
[
'@' . $this->prefix('psrWorker'),
'@' . $this->prefix('chain'),
'@' . $this->prefix('events'),
]
);
->setFactory(
RoadRunner::class,
[
'@' . $this->prefix('psrWorker'),
'@' . $this->prefix('handler'),
'@' . $this->prefix('events'),
]
);
}

protected function createMiddlewareChain(ContainerBuilder $builder)
{
$this->createServiceDefinition($builder, 'chain')
->setFactory(PsrChain::class, [
new Nette\PhpGenerator\Literal('new \Nyholm\Psr7\Response'),
...$this->config->middlewares,
'@' . $this->prefix('application'),
]);
$this->createServiceDefinition($builder, 'initializeMiddleware')
->setAutowired(false)
->setFactory(NetteInitializeMiddleware::class);

$this->createServiceDefinition($builder, 'handler')
->setAutowired(false)
->setFactory(PsrChain::class, [
'@' . $this->prefix('application'),
'@' . $this->prefix('initializeMiddleware'),
...$this->config->middlewares,
]);
}
}
29 changes: 29 additions & 0 deletions src/Middlewares/NetteInitializeMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Mallgroup\RoadRunner\Middlewares;

use Mallgroup\RoadRunner\Http\IRequest;
use Mallgroup\RoadRunner\Http\IResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class NetteInitializeMiddleware implements MiddlewareInterface
{
public function __construct(
private IRequest $httpRequest,
private IResponse $httpResponse,
) {
}

public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler,
): ResponseInterface {
$this->httpResponse->cleanup();
$this->httpRequest->updateFromPsr($request);

return $handler->handle($request);
}
}
38 changes: 38 additions & 0 deletions src/Middlewares/OutputBufferMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Mallgroup\RoadRunner\Middlewares;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;

class OutputBufferMiddleware implements MiddlewareInterface
{
public function __construct(
private ?LoggerInterface $logger = null,
) {
}

public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler,
): ResponseInterface {
try {
ob_start();
return $handler->handle($request);
} finally {
$content = ob_get_clean();
if ($content) {
$this->logger?->warning(
'Unexpected output found on request, you are pushing to output instead of Response',
[
'length' => strlen($content),
'content' => substr($content, 0, 300) . (strlen($content) > 300 ? '... (shorted)' : ''),
],
);
}
}
}
}
63 changes: 63 additions & 0 deletions src/Middlewares/SessionMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);

namespace Mallgroup\RoadRunner\Middlewares;

use Nette\Http\IResponse;
use Nette\Http\Session;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;

class SessionMiddleware implements MiddlewareInterface
{
public function __construct(private Session $session, private IResponse $response)
{
$this->session->setOptions($this->session->getOptions() + ['cache_limiter' => '']);
}

/**
* Process a server request and return a response.
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (session_status() === PHP_SESSION_ACTIVE || session_id() || !empty($_SESSION)) {
throw new RuntimeException('Potential session leak.');
}

$this->session->start();

$this->sendCookie();

try {
return $handler->handle($request);
} finally {
$this->session->close();

// convince Nette Session to read the cookies
session_id('');
// clear the previous session
$_SESSION = [];
}
}

/**
* Sends the session cookies.
*/
private function sendCookie(): void
{
$cookie = session_get_cookie_params();
$this->response->setCookie(
session_name(),
session_id(),
$cookie['lifetime'] ? $cookie['lifetime'] + time() : 0,
$cookie['path'],
$cookie['domain'],
$cookie['secure'],
$cookie['httponly'],
$cookie['samesite'] ?? null,
);
}
}
40 changes: 0 additions & 40 deletions src/Middlewares/TracyMiddleware.php

This file was deleted.

0 comments on commit 66ea0c5

Please sign in to comment.