Skip to content

[HttpKernel] "Deprecation" of StreamedResponseListener causes RequestStack to be empty within StreamedResponse callback #46743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
DaDeather opened this issue Jun 23, 2022 · 13 comments
Labels
Bug Help wanted Issues and PRs which are looking for volunteers to complete them. HttpKernel Status: Needs Review

Comments

@DaDeather
Copy link
Contributor

Symfony version(s) affected

6.1.*

Description

The change made within #45476 causes the RequestStack to be empty when the StreamedResponse Callback is being called.

In detail:
When using an StreamedResponse, the Callback passed to StreamedResponse is executed AFTER the \Symfony\Component\HttpKernel\Kernel::handle method (which is doing a pop on the RequestStack in \Symfony\Component\HttpKernel\HttpKernel::finishRequest).

Instead it is executed within the \Symfony\Component\HttpFoundation\Response::send method causing the \Symfony\Component\HttpFoundation\RequestStack to be empty and not having any Request object inside it.

We're upgrading our legacy application which is already wrapped with symfony.
This is why we're sadly have to use a construct where we're required to inject the \Symfony\Component\HttpFoundation\RequestStack to get the Request which is not available anymore at that state within a StreamedResponse.

Is this really the expected / wanted behaviour for this change? That when using a StreamedResponse the Request object becomes unavailable or is required to be reinitialized manually? If this is desired it should be announced as a possible BC break.

How to reproduce

./src/Service/TestService.php

<?php

namespace App\Service;

use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

#[Autoconfigure(public: true)]
class TestService
{
    public function __construct(private readonly RequestStack $requestStack)
    {
    }

    public function getRequest(): Request
    {
        return $this->requestStack->getMainRequest();
    }
}

./src/Controller/TestController.php

<?php

namespace App\Controller;

use App\Kernel;
use App\Service\TestService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;

#[AsController]
class TestController extends AbstractController
{
    #[Route('/')]
    public function test(Kernel $kernel): StreamedResponse
    {
        return new StreamedResponse(function () use ($kernel) {
            /** @var TestService $testService */
            $testService = $kernel->getContainer()->get(TestService::class);

            $mainRequest = $testService->getRequest();

            echo $mainRequest->server->get('HTTP_HOST');
        });
    }
}

When calling the /test url an exception is thrown:

Return value must be of type Symfony\Component\HttpFoundation\Request, null returned

Possible Solution

No response

Additional Context

Behaviour was changed in pull request: #45476

@mmarton
Copy link

mmarton commented Jun 25, 2022

semi related #46445

@alexander-schranz
Copy link
Contributor

@DaDeather interesting does this also mean that app.request is empty when you do:

return new StreamedResponse(function () use ($twig) {
    $twig->display('template.html.twig');
});

?

@DaDeather
Copy link
Contributor Author

@DaDeather interesting does this also mean that app.request is empty when you do:

return new StreamedResponse(function () use ($twig) {
    $twig->display('template.html.twig');
});

?

Yes, exactly.
Since the app.request is calling \Symfony\Component\HttpFoundation\RequestStack::getCurrentRequest it is returning null instead of the request object.

@alexander-schranz
Copy link
Contributor

alexander-schranz commented Jun 27, 2022

Maybe something like the following in the HttpKernel would be required to fix this issue and not lose compatibility to roadrunner or swoole streamed response:

if ($response instance of StreamedResponse) {
    $callback = $response->getCallback();
    $response->setCallback(function() use ($callback) {
        try {
             $callback();
        } finally {
             $this->requestStack->pop();
        }
    });
} else {
    $this->requestStack->pop();
}

/cc @nicolas-grekas what do you think?

The #46445 would require another fix in the browserkit to convert StreamedResponse into browserkit response correctly.

@pableu
Copy link
Contributor

pableu commented Aug 17, 2022

I was also affected by this in our application. We have a lot of legacy code that we simply wrapped in StreamedResponse-callbacks (similar to what's described in https://symfony.com/doc/current/migration.html). This broke in 6.1 because the RequestStack is empty inside the callback, which means the callback can't access the Session or make Sub-Requests.

I decided to simply re-add a clone of the StreamedResponseListener for the time being:

<?php
declare(strict_types=1);

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class MyStreamedResponseListener implements EventSubscriberInterface
{

  public function onKernelResponse(ResponseEvent $event)
  {
    if (!$event->isMainRequest()) {
      return;
    }

    $response = $event->getResponse();

    if ($response instanceof StreamedResponse) {
      $response->send();
    }
  }

  public static function getSubscribedEvents(): array
  {
    return [
        KernelEvents::RESPONSE => ['onKernelResponse', -1024],
    ];
  }
}

This seems to work fine for us. I just rolled out the fix today.

@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@pableu
Copy link
Contributor

pableu commented Mar 4, 2023

@carsonbot yes it is still relevant and also present in Symfony 6.2.7.

My workaround above works, but I still consider this a bug in the framework and a BC break in Symfony 6.1.

@carsonbot carsonbot removed the Stalled label Mar 4, 2023
@nickygerritsen
Copy link

We are also running into this. We use streamed responses for processes that take a while, where we show a progress bar in the UI. However, we then can't add any flashes anymore. I don't really like the old workaround, since clearly there is a reason Symfony doesn't do that anymore. I hope we can find a decent solution.

@dduenker
Copy link

still relevant in 6.2.9

@nicolas-grekas
Copy link
Member

@alexander-schranz up to give a PR a try?
Anyone else?

@nicolas-grekas nicolas-grekas added the Help wanted Issues and PRs which are looking for volunteers to complete them. label Jul 27, 2023
@DaDeather
Copy link
Contributor Author

I have opened up a PR for this issue based on @alexander-schranz code.

@nicolas-grekas
Copy link
Member

I think we completely missed this consequence. It'd be fine reverting #45476, and adding also a test case that covers this situation so that we won't forget about the use case again.

@DaDeather
Copy link
Contributor Author

I've created a PR to revert #45476 and add a test to it:
#51391

nicolas-grekas added a commit that referenced this issue Aug 16, 2023
…medResponse (Ismail Turan)

This PR was merged into the 6.3 branch.

Discussion
----------

[HttpKernel] Fix missing Request in RequestStack for StreamedResponse

| Q             | A
| ------------- | ---
| Branch?       | 6.3
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #46743
| License       | MIT
| Doc PR        | -

Similar to #51139 by `@DaDeather`, see discussion there.

Commits
-------

ca1c40e [HttpKernel] Fix missing Request in RequestStack for StreamedResponse
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Help wanted Issues and PRs which are looking for volunteers to complete them. HttpKernel Status: Needs Review
Projects
None yet
8 participants