Skip to content

[HttpFoundation] static Request::$formats can't be reset which causes issues in worker mode #59036

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
soyuka opened this issue Nov 29, 2024 · 7 comments

Comments

@soyuka
Copy link
Contributor

soyuka commented Nov 29, 2024

Symfony version(s) affected

6.4, 7.1

Description

In API Platform, we need to add formats per-request. Indeed, each HTTP operation can be configured with specific mime-types.

To do so, we use Request::setFormat:

public function setFormat(?string $format, string|array $mimeTypes): void
{
if (null === static::$formats) {
static::initializeFormats();
}
static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes];

We recently stumbled on an issue in worker mode. Indeed as $formats is static, its values are sometimes the same as a previous request and we need to merge new formats (api-platform/core#6833) to bypass this issue.

Possible Solution

With @dunglas we were thinking of a few solutions:

  • either allow a way to restore Request::$formats in a ResetInterface to null (although this solution makes the static not so useful)
  • set a local variable (Request::$currentFormat) that would fallback on the static value if not defined. This would allow to set formats per-request.

Note: to reproduce we can use api-platform/core#6831 or the test at api-platform/core#6833

@dwgebler
Copy link

Request isn't really a service so I probably wouldn't recommend bringing ResetInterface into the mix.

The idea of Request::$currentFormat is probably more feasible, but note the specific nature of the issue is that once a mapping for a format to its MIME types, like in this case the change of 'json' => ['application/json'] to 'json' => ['application/problem+json'], it is the subsequent call to getFormat('application/json') that fails, and in the case of API Platform bug I experienced, this call in worker mode was being made by JsonLoginAuthenticator::supports - something that sits outside API Platform, where it would have no way to inject a $currentFormat (except with a sufficiently high priority kernel listener). I think this might get messy in different applications or bundles interacting with each other.

I wonder if the better solution is to just a provide a wrapper in Request on initializeFormats that products like API Platform can call at the end of each request in their own kernel.terminate listeners, e.g. public reinitializeFormats(): void

Or the same approach but instead of leaving it to applications to take care of resetting this state, Symfony's kernel should reinitialize formats automatically on Request at the end of each main request.

@stof
Copy link
Member

stof commented Nov 29, 2024

I'm wondering why ApiPlatform overrides the mime type associated with the json format. This seems the root cause of the issue to me

@dwgebler
Copy link

I'm wondering why ApiPlatform overrides the mime type associated with the json format. This seems the root cause of the issue to me

@stof

In a sense, it is, and it's a bug in API Platform for which I've submitted a patch.

But I agree with @soyuka and @dunglas that given Symfony provides a public interface to change these mappings in Request, it's reasonable that Symfony as a framework intended to work with different runtimes should also provide a means to clean up that state between requests in long-running workers.

@soyuka
Copy link
Contributor Author

soyuka commented Nov 29, 2024

I'm wondering why ApiPlatform overrides the mime type associated with the json format. This seems the root cause of the issue to me

This is the point of the setFormat method, to be able to change mime types for specific formats. We do this for errors but also for many other json formats (json-ld, hal, openapi, json api). I think that it'd be nice to be able to use setFormat without having issues with workers like swoole or FrankenPHP as it's an annoying bug to track down.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Nov 29, 2024

We can solve this either in HttpKernel or in the runtime component. Which one would make most sense?

@dwgebler
Copy link

dwgebler commented Nov 29, 2024

We can solve this either in HttpKernel or in the runtime component. Which one would make most sense?

In HttpKernel::terminate I'd have thought, or wherever it is services tagged with kernel.reset get called - certainly I think that's the point in the execution flow you'd want this to be cleaned up.

@dunglas
Copy link
Member

dunglas commented Nov 29, 2024

@nicolas-grekas it's better to do it in HttpKernel, otherwise we'll have to patch all runtimes.

nicolas-grekas added a commit that referenced this issue Jan 9, 2025
…using the service resetter (nicolas-grekas)

This PR was merged into the 6.4 branch.

Discussion
----------

[FrameworkBundle][HttpFoundation] Reset Request's formats using the service resetter

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | #59036
| License       | MIT

Commits
-------

56f3df4 [HttpFoundation][FrameworkBundle] Reset Request's formats using the service resetter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment