Bug #81516 [Fbk]: Curl used with CURLOPT_WRITEFUNCTION rarely corrupts the response

From: Date: Wed, 13 Oct 2021 23:19:39 +0000
Subject: Bug #81516 [Fbk]: Curl used with CURLOPT_WRITEFUNCTION rarely corrupts the response
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-237188@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=81516&edit=1

 ID:                 81516
 Updated by:         requinix@php.net
 Reported by:        roland at nextendweb dot com
 Summary:            Curl used with CURLOPT_WRITEFUNCTION rarely corrupts
                     the response
 Status:             Feedback
 Type:               Bug
 Package:            cURL related
 PHP Version:        7.4.24
 Block user comment: N
 Private report:     N

 New Comment:

Weird. What happens if you have PHP output the data instead and then pipe that into cat? Or use some
other methods of writing out to a file?

<?php
$value = '...';
echo substr(base64_decode($value), 0, 1100);
?>

$ php nowrite.php | cat > b.txt


Previous Comments:
------------------------------------------------------------------------
[2021-10-13 17:05:58] roland at nextendweb dot com

It looks like these are some kind of special systems which are unable to write the following data to
the filesystem and this is why that chunk is missing always.

https://gist.github.com/nextend/91b09c70a5a86fff34bcf87e29c9f068

Output is NULL and the file is not created in the filesystem. No error thrown.

------------------------------------------------------------------------
[2021-10-13 16:49:03] roland at nextendweb dot com

https://gist.github.com/nextend/8c0d752024bac9c0a030d5ba022dfac9

There is a chunk which is not written into the stream. The output is the following:
/ 1160
Chunk #4430: 781473d903bedf95d8a07d34974ac548 1160
Good md5: 231732259d67fe83ed6fc02d7ad9be57
Stream md5: 4a3b968a44c585a2883e687d61c251fb
Memory md5: 231732259d67fe83ed6fc02d7ad9be57

The return value of fwrite() for this chunk is NULL.

------------------------------------------------------------------------
[2021-10-13 16:35:59] roland at nextendweb dot com

The following gist works like a charm and the md5 of the data is fine without the PHP stream write:

https://gist.github.com/nextend/61bb5a8b36ea126f3e95297f3f7de10e


I tried, but it takes forever to finish:
  for ($len = 0; $len < $data_length; ) {
    $len += fwrite($this->stream_handle, substr($data, $len));
  }

------------------------------------------------------------------------
[2021-10-13 08:15:11] roland at nextendweb dot com

Thank you for your tips!

As you wrote CURLOPT_BUFFERSIZE is just a request and not an order, so it might happen that a part
of the response is missing and this is why the md5 hashes are different for several chunks. So I can
see why my check of the chunk's md5 hashes might not the right tool for debugging this.

Expected response at buffersize 4:
aaaa
bbbb
cccc
dddd
-> response hash OK

Valid response at buffersize 4:
aaaa
bbb    -> chunk hash mismatch
bcccc  -> chunk hash mismatch
dddd
-> response hash OK

Not valid response at buffersize 4:
aaaa
bb     -> chunk hash mismatch
ccc    -> chunk hash mismatch
dddd
-> response hash FAIL

I think the latter happens, but I will do more debugging with the length of the chunks.

Currently I do not have access to the server which produced this error, but will try to get one. We
know about 10 different servers which produced this and they are were the very same response MD5 
hash, so it could not be a coincident.

------------------------------------------------------------------------
[2021-10-12 18:49:18] requinix@php.net

Can you modify your script to also include the data length in the log output?

After doing that and running the script (and if seeing the length doesn't identify the
problem), can you additionally modify the fwrite() statement to be a loop:

  for ($len = 0; $len < $data_length; ) {
    $len += fwrite($this->stream_handle, substr($data, $len));
  }

This is because fwrite() does not guarantee to write its entire string at once, and will return the
number of bytes it actually wrote. This is almost never an issue with writing to a local file,
though...

Back to the data length. The output shows those 40 chunks *present* but with different hashes. Your
original report said that 1160 bytes were *missing*. Does that mean you're seeing two different
behaviors between the WP code and your script?
Because if cURL is giving the write function 1160 bytes, that means your original report had a
problem with the callback 1 time while your new script had a problem with the callback 40 times.

Speaking of, note that the buffer size is "a request, not an order", so the chunks
received by the callback may not be exactly that size.
https://curl.se/libcurl/c/CURLOPT_BUFFERSIZE.html

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=81516


--
Edit this bug report at https://bugs.php.net/bug.php?id=81516&edit=1


Thread (14 messages)

« previous php.bugs (#237188) next »