Tyrel
Tyrel

Reputation: 737

PHP: How to read POST body with Transfer-Encoding: chunked, no Content-Length

I am writing a service to receive data from an energy monitoring device in my house. The only way the device can push data is by using an HTTP POST, with Transfer-Encoding: chunked, and no Content-Length, and the data as XML in the request body.

I would like to be able to read this data in a PHP script (behind Apache) and store it in my database.

It appears that the most correct way to read the HTTP request body in PHP these days is to open and read php://input. However, comments from users on PHP's website indicate (and my testing confirms), that if no Content-Length header is specified by the client, php://input returns no data (even if there is data being sent by the client in chunked encoding).

Is there an alternate way to get access to the request body? Can I configure Apache (with .htaccess) to decode the chunked data and call my script once it gets it all, and include Content-Length? Or configure PHP so it will be able to handle this?

Thanks!

Upvotes: 5

Views: 5070

Answers (2)

andraaspar
andraaspar

Reputation: 876

I faced a similar issue recently with Apache 2.4.58 and PHP 8.2.15 (empty $_POST when the Content-Encoding: chunked header is persent), and fixed it by adding the following line in the Apache configuration:

SetEnv proxy-sendcl 1

This configuration is related to mod_proxy, which is used to handle PHP FPM. The line above should be added near ProxyPass (something similar is seen in the mod_proxy docs):

<Location "/buggyappserver/">
  ProxyPass "http://buggyappserver:7001/foo/"
  SetEnv proxy-sendcl 1
</Location>

Or as in my case (in Amazon Lightsail / Bitnami) near the SetHandler "proxy:..." directive in /opt/bitnami/apache/conf/bitnami/php-fpm.conf, like this:

<IfDefine USE_PHP_FPM>
  <Proxy "unix:/opt/bitnami/php/var/run/www.sock|fcgi://www-fpm" timeout=300>
  </Proxy>
  <FilesMatch \.php$>
    <If "-f %{REQUEST_FILENAME}">
      SetEnv proxy-sendcl 1
      SetHandler "proxy:fcgi://www-fpm"
    </If>
  </FilesMatch>
</IfDefine>

Related Apache issues:

But since you asked about php://input, I will add that according to the PHP docs:

php://input is not available in POST requests with enctype="multipart/form-data" if enable_post_data_reading option is enabled.

So be sure to check that option too.

Upvotes: 2

ficuscr
ficuscr

Reputation: 7054

Might as well write this as an answer I suppose.

The issue you describe is documented here: https://bugs.php.net/bug.php?id=60826

Highlights from the thread:

So here is what I found. If you send chunked http request but do not send the content-length (as we should do using HTTP 1.1 protocol) the body part of the request is empty (php://input returns nothing). This result remains valid for the following configuration where PHP runs via FastCGI :

The same configuration with PHP running as mod_php is fine, the body request is available through php://input.

And

Essentially, PHP is following the spec, and not reading beyond the FastCGI CONTENT_LENGTH header value - so correct fixes are necessarily above PHP, in the FastCGI or web server implementation.

Possible Workarounds:

0) Use httpd with mod_php

1) Use a different web server.

2) Look at patches for mod_fcgid

3) Maybe (not recommended) flip always_populate_raw_post_data and see if there is anything in $HTTP_RAW_POST_DATA

Upvotes: 6

Related Questions