Search code examples
phptwigslimslim-4

Modify routes and url when using Varnish


I have a project where Varnish is used in front of a Slim 4 project. Due to some project specialities, the original url should be hidden and the incoming request is rewritten to some other headers.

Example

  • Original call to demo.xyz is coming to Varnish
  • Varnish is transforming the request to http://slimapp.dev/url/demo.xyz
  • The original host is saved in the header with X-Orig-Host: demo.xyz

The Slim App needs to detect if it is a Varnish request (I already got that) and needs to transform the routes.

Routes

Without Varnish:

$app->group('/url/{url}', function (RouteCollectorProxy $group) {
    $group->get('[/city/{city}]', IndexAction::class)->setName('home');
    $group->get('/search[/{query}]', SearchQueryAction::class)->setName('search-query');
});

With Varnish:

$app->get('[/city/{city}]', IndexAction::class)->setName('home');
$app->get('/search[/{query}]', SearchQueryAction::class)->setName('search-query');

So when creating a route with url_for (Twig-View package) the urls should be generated like that:

  • Route w/o Varnish: http://slimapp.dev/url/demo.xyz/article/123
  • Route w/ Varnish: http://demo.xyz/article/123

Ideas:

  1. Modify the original UriInterface object but I have no idea how.
  2. Write a TwigExtension for Twig-View to modify the methods for url_for and so on.

The first ideas feels kind of more reliable and the right place to do. The other one would mean I would need to overwrite the helper methods to build the url based on the X-Orig-Host.

Any idea how I can modify the Uri / probably overwriting the create method?

Thanks in advance!

P. S.: The same questions I placed in Slim Discourse.

UPDATE 1

routes.php

return function (App $app) {

    $app->get('/', function () {
        die('No URL defined.');
    })->setName('root');

    $app->group('/url/{url}', function (RouteCollectorProxy $group) {
        $group->get('[/city/{city}]', IndexAction::class)->setName('home');

        # Internal page search
        $group->get('/search[/{query}]', SearchQueryAction::class)->setName('search-query');
    });

};

middleware.php

return function(App $app) {
    $c = $app->getContainer();

    $app->addBodyParsingMiddleware();
    $app->add(TwigMiddleware::class);
    $app->addRoutingMiddleware();
    $app->add(VarnishBasePathMiddleware::class);

    $displayErrorDetails = $c->get(Configuration::class)->getBool('displayErrorDetails');
    $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, false, false);
    $errorHandler = $errorMiddleware->getDefaultErrorHandler();
    $errorHandler->registerErrorRenderer('text/html', HtmlErrorRenderer::class);
    $errorHandler->registerErrorRenderer('application/json', JsonErrorRenderer::class);
};

Error message

[404] Not found. in /usr/share/nginx/html/vendor/slim/slim/Slim/Middleware/RoutingMiddleware.php on line 91.

Backtrace: #0 /usr/share/nginx/html/vendor/slim/slim/Slim/Middleware/RoutingMiddleware.php(57): Slim\Middleware\RoutingMiddleware->performRouting(Object(Slim\Psr7\Request)) #1 /usr/share/nginx/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(132): Slim\Middleware\RoutingMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #2 /usr/share/nginx/html/src/Middleware/VarnishBasePathMiddleware.php(28): class@anonymous->handle(Object(Slim\Psr7\Request)) #3 /usr/share/nginx/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(180): App\Middleware\VarnishBasePathMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #4 /usr/share/nginx/html/vendor/slim/twig-view/src/TwigMiddleware.php(125): class@anonymous->handle(Object(Slim\Psr7\Request)) #5 /usr/share/nginx/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(180): Slim\Views\TwigMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #6 /usr/share/nginx/html/vendor/slim/slim/Slim/Middleware/ErrorMiddleware.php(89): class@anonymous->handle(Object(Slim\Psr7\Request)) #7 /usr/share/nginx/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(132): Slim\Middleware\ErrorMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #8 /usr/share/nginx/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(73): class@anonymous->handle(Object(Slim\Psr7\Request)) #9 /usr/share/nginx/html/vendor/slim/slim/Slim/App.php(208): Slim\MiddlewareDispatcher->handle(Object(Slim\Psr7\Request)) #10 /usr/share/nginx/html/public/index.php(57): Slim\App->handle(Object(Slim\Psr7\Request)) #11 {main}

Solution

  • You can implement you own ServerRequestCreatorInterface class like this:

    use Psr\Http\Message\ServerRequestInterface;
    use Slim\Factory\ServerRequestCreatorFactory;
    use Slim\Interfaces\ServerRequestCreatorInterface;
    use Slim\Psr7\Factory\ServerRequestFactory;
    
    class VarnishServerRequestCreator implements ServerRequestCreatorInterface
    {
        public function createServerRequestFromGlobals(): ServerRequestInterface
        {
            $request = ServerRequestFactory::createFromGlobals();
            $serverParams = $request->getServerParams();
    
            if (isset($serverParams['X-Orig-Host'])) {
                // Replace the hostname
                $uri = $request->getUri()->withHost($serverParams['X-Orig-Host']);
                $request = $request->withUri($uri);
            }
    
            return $request;
        }
    }
    
    ServerRequestCreatorFactory::setServerRequestCreator(new VarnishServerRequestCreator());
    

    To output the full url in your Twig templates use the full_url_for function. Example:

    {{ full_url_for('article') }}
    

    Output:

    https://demo.xyz/article

    To change the base path you could add this special middleware:

    <?php
    
    namespace App\Middleware;
    
    use Psr\Http\Message\ResponseInterface;
    use Psr\Http\Message\ServerRequestInterface;
    use Psr\Http\Server\MiddlewareInterface;
    use Psr\Http\Server\RequestHandlerInterface;
    use Slim\App;
    
    final class VarnishBasePathMiddleware implements MiddlewareInterface
    {
        /**
         * @var App The slim app
         */
        private $app;
    
        public function __construct(App $app)
        {
            $this->app = $app;
        }
    
        public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
        {
            $serverParams = $request->getServerParams();
    
            // Detect Varnish
            if (isset($serverParams['X-Orig-Host'])) {
                // Change the base path
                $this->app->setBasePath('/url/demo.xyz');
            }
    
            return $handler->handle($request);
        }
    }
    

    The VarnishBasePathMiddleware must be add after the RoutingMiddleware

    $app->add(TwigMiddleware::class);
    
    $app->addRoutingMiddleware();
    
    $app->add(\App\Middleware\VarnishBasePathMiddleware::class); // <--- here
    
    $app->addErrorMiddleware(...)