Skip to content
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

Guzzle adds an empty header on multipart POST request containing an animated GIF #2905

Closed
barnabywalters opened this issue Jun 18, 2021 · 5 comments
Labels
lifecycle/stale No activity for a long time

Comments

@barnabywalters
Copy link

Guzzle version(s) affected: 7.3, dev-master
PHP version: 7.4.19
cURL version: 7.77.0

Description
When used to make a multipart POST request containing an animated GIF, the guzzle client adds an HTTP header consisting only of whitespace (6 spaces in the name, 12 in the value). This invalid header causes server errors when using strict HTTP message implementations such as nyholm/psr7.

How to reproduce

$client = new GuzzleHttp\Client();
$options = [
  'multipart' => [
    [
      'name' => 'file',
      'contents' => fopen('/path/to/an/animated.gif', 'r'), // e.g. https://micropub.rocks/media/w3c-socialwg.gif
      'filename' => 'img.gif'
    ]
  ]
];
$res = $client->request('POST', 'https://micropub.taprootproject.com/upload.php', $options);

results in apache_request_headers() looking like this:

Array
(
    [Host] => micropub.taprootproject.com
    [      ] =>             
    [User-Agent] => GuzzleHttp/7
    [Content-Type] => multipart/form-data; boundary=fbd6adfea2ba76bcfbd21b673ab289ec6e65c7be
    [Content-Length] => 1509659
)

…and $_SERVER containing this key/value:

[HTTP_______] =>             

For the moment, I’ll leave my test page https://micropub.taprootproject.com/upload.php as it is, a no-op which prints the results of apache_request_headers().

Additional context
This seems to occur only when uploading animated gifs. I tried two different animated gifs and one static gif. Both animated gifs caused the empty header, the static one did not.

Making the same request via command line curl 7.64.1 doesn’t include the mysterious header: shell commands and output

When using nyholm/psr7-server, a request containing invalid headers such as this one results in the following stack trace:

[18-Jun-2021 17:38:41 UTC] PHP Fatal error:  Uncaught InvalidArgumentException: Header name must be an RFC 7230 compatible string. in /home/•••/domains/micropub.taprootproject.com/vendor/nyholm/psr7/src/MessageTrait.php:179
Stack trace:
#0 /home/•••/domains/micropub.taprootproject.com/vendor/nyholm/psr7/src/MessageTrait.php(146): Nyholm\Psr7\ServerRequest->validateAndTrimHeader('      ', '            ')
#1 /home/•••/domains/micropub.taprootproject.com/vendor/nyholm/psr7/src/MessageTrait.php(98): Nyholm\Psr7\ServerRequest->setHeaders(Array)
#2 /home/•••/domains/micropub.taprootproject.com/vendor/nyholm/psr7-server/src/ServerRequestCreator.php(90): Nyholm\Psr7\ServerRequest->withAddedHeader('      ', '            ')
#3 /home/•••/domains/micropub.taprootproject.com/vendor/nyholm/psr7-server/src/ServerRequestCreator.php(71): Nyholm\Psr7Server\ServerRequestCreator->fromArrays(Array, Array, Array, Array, Array, Array, Resource id #127)
#4 /home/•••/domains/micropub.taprootproject.com/vendor/slim/slim/Slim/Factory/Psr1 in /•••/domains/micropub.taprootproject.com/vendor/nyholm/psr7/src/MessageTrait.php on line 179
@barnabywalters
Copy link
Author

Tagging @Zegnat (nyholm/psr7 maintainer) as they wanted to keep an eye on this issue

@barnabywalters
Copy link
Author

It looks like the Accept header is being turned into whitespace for some reason. Here’s apache_request_headers() for a valid upload via the curl command line:

Array
(
    [Host] => micropub.taprootproject.com
    [User-Agent] => curl/7.77.0
    [Accept] => */*
    [Content-Length] => 1509642
    [Content-Type] => multipart/form-data; boundary=------------------------ed9fa61d744f7205
)

@Zerg2000
Copy link

Zerg2000 commented Jun 23, 2021

What HTTP server is being used? I have the same problem, but with "Expect" header on LiteSpeed and PHP 7.4 (LSAPI).

Here is request made by Guzzle:

POST /test.php HTTP/1.1
Host: (...)
Expect: 100-Continue
User-Agent: GuzzleHttp/7
Content-Type: multipart/form-data; boundary=0118d7b42a81b7f2fc6233de7c801bd4d6ebad75
Authorization: Bearer Test
Content-Length: 3537424

and apache_request_headers() result:

Array
(
    [Host] => (...)
    [      ] =>             
    [User-Agent] => GuzzleHttp/7
    [Content-Type] => multipart/form-data; boundary=0118d7b42a81b7f2fc6233de7c801bd4d6ebad75
    [Authorization] => Bearer Test
    [Content-Length] => 3537424
)

"Expect" header is being zeroed with spaces instead of being removed. Switching to PHP 8.0 seems to fix the problem, but PHP 8.0 is not using "Expect" header. Currently it can be worked around with:

$client = new \GuzzleHttp\Client(['expect' => false]);

I suspect it might be related to LiteSpeed or LSAPI.

@barnabywalters
Copy link
Author

Very interesting! The Expect header looks like a more likely cause than Accept, and there could also be legitimate reasons for it being removed from the headers at some point in the chain. I suppose it’s not being added due to the uploaded file specifically being an animated gif, but because it’s over some size threshold.

A quick search doesn’t show any reports of similar bugs due to the header being changed to spaces, so it’s tricky to find exactly where it’s happening. At least it doesn’t look like a Guzzle bug, and we have a few workarounds now — thanks @Zerg2000!

@stale
Copy link

stale bot commented Oct 22, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 2 weeks if no further activity occurs. Thank you for your contributions.

@stale stale bot added the lifecycle/stale No activity for a long time label Oct 22, 2021
@stale stale bot closed this as completed Nov 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lifecycle/stale No activity for a long time
Projects
None yet
Development

No branches or pull requests

2 participants