Skip to content

[HttpFoundation] Prevent BinaryFileResponse::prepare from adding content type if no content is sent #47516

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

Merged
merged 1 commit into from
Sep 13, 2022

Conversation

stollr
Copy link

@stollr stollr commented Sep 7, 2022

Q A
Branch? 4.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #47473
License MIT
Doc PR -

Until now, BinaryFileResponse::prepare() adds Content-Type and other content related header even if no content is sent. This happens for example if the provided file's modification date is not older than the cached version of the client and http status 304 is sent. See issue #47473

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Sep 8, 2022

Good catch. Can you please try the following patch? It might fix a few more things.

--- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
+++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
@@ -210,11 +210,13 @@ class BinaryFileResponse extends Response
             $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
         }
 
-        if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
-            $this->setProtocolVersion('1.1');
-        }
+        parent::prepare($request);
+
+        if ($this->isInformational() || $this->isEmpty()) {
+            $this->maxlen = 0;
 
-        $this->ensureIEOverSSLCompatibility($request);
+            return $this;
+        }
 
         $this->offset = 0;
         $this->maxlen = -1;
@@ -222,6 +224,7 @@ class BinaryFileResponse extends Response
         if (false === $fileSize = $this->file->getSize()) {
             return $this;
         }
+        $this->headers->remove('Transfer-Encoding');
         $this->headers->set('Content-Length', $fileSize);
 
         if (!$this->headers->has('Accept-Ranges')) {
@@ -291,6 +294,10 @@ class BinaryFileResponse extends Response
             }
         }
 
+        if ($request->isMethod('HEAD')) {
+            $this->maxlen = 0;
+        }
+
         return $this;
     }
 
@@ -312,6 +319,7 @@ class BinaryFileResponse extends Response
      */
     public function sendContent()
     {
+        try {
             if (!$this->isSuccessful()) {
                 return parent::sendContent();
             }
@@ -343,10 +351,11 @@ class BinaryFileResponse extends Response
 
             fclose($out);
             fclose($file);
+        } finally {
             if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
                 unlink($this->file->getPathname());
             }
+        }
 
         return $this;
     }

@stollr
Copy link
Author

stollr commented Sep 9, 2022

@nicolas-grekas Thanks for your review and improvements. They are much cleaner! There is just one part where my commit differs from your suggestion.

I have added the ($this->isInformational() || $this->isEmpty()) check and the return before the content type is setted, to reduce some needless instructions.

@carsonbot carsonbot changed the title Prevent BinaryFileResponse::prepare from adding content type if no content is sent [HttpFoundation] Prevent BinaryFileResponse::prepare from adding content type if no content is sent Sep 12, 2022
@fabpot
Copy link
Member

fabpot commented Sep 13, 2022

Thank you @Naitsirch.

@fabpot fabpot merged commit 2f7c7bb into symfony:4.4 Sep 13, 2022
fabpot added a commit that referenced this pull request Sep 30, 2022
…tion logic (X-Coder264)

This PR was merged into the 4.4 branch.

Discussion
----------

[HttpFoundation] Fix BinaryFileResponse content type detection logic

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

I've just upgraded to 5.4.13 (from 5.4.8) and a lot of my tests started failing. Upon further debugging I've found the issue to be caused by #47516

Prior to that PR

```php
        if (!$this->headers->has('Content-Type')) {
            $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
        }
```

was called before the `parent::prepare($request);` call.

After that PR `parent::prepare($request);` was called first and that would call

```php
            // Content-type based on the Request
            if (!$headers->has('Content-Type')) {
                $format = $request->getRequestFormat(null);
                if (null !== $format && $mimeType = $request->getMimeType($format)) {
                    $headers->set('Content-Type', $mimeType);
                }
            }
```

which would set the content type from the request so by the time

```php
        if (!$this->headers->has('Content-Type')) {
            $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
        }
```

is executed again in the BinaryFileResponse `prepare` method that `if` statement never evaluates to `true` because the `Content-Type` header was already set (to the wrong one).

This PR fixes this issue by making sure the order of the method calls is correct.

Commits
-------

87f58ae Fix BinaryFileResponse content type detection logic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants