Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Stream] STREAM_NOTIFY_PROGRESS over HTTP emitted irregularly for last chunk of data #10031

Closed
ThePHPF/thephp.foundation
#90
@aetonsi

Description

@aetonsi

Description

Problem

Files

fileecho.php
<?php$fsize =1000;$chunksize =99;$chunks =floor($fsize /$chunksize);// 10 chunks * 99 bytes$lastchunksize =$fsize -$chunksize *$chunks;// 1 chunk * 10 bytesheader("Content-length:" .$fsize);flush();for ($chunk =1;$chunk <=$chunks;$chunk++) {echostr_repeat('x',$chunksize);    @ob_flush();usleep(50 *1000);}echostr_repeat('x',$lastchunksize);
filetest.php
<?php$context =stream_context_create(['http' => ['ignore_errors' =>true,]]);$lastBytesTransferred =0;stream_context_set_params($context, ['notification' =>function ($code,$s,$m,$mc,$bytes_transferred,$bytes_max)use (&$lastBytesTransferred) {$progress =$bytes_max ?$bytes_transferred /$bytes_max *100 :0;$chunksize =$bytes_transferred -$lastBytesTransferred;if ($code ===STREAM_NOTIFY_PROGRESS)echo"\nprogress:$progress% ($bytes_transferred/$bytes_max b) (chunksize=$chunksize)";if ($code ===STREAM_NOTIFY_FILE_SIZE_IS)echo"\nexpected filesize=$bytes_max";if ($code ===STREAM_NOTIFY_COMPLETED)echo"\nSTREAM_NOTIFY_COMPLETED";$lastBytesTransferred =$bytes_transferred;    @ob_flush();}]);$get =file_get_contents('http://localhost:8000/echo.php',false,$context);if (!$get)die("\nERROR during file_get_contents. is the local server running? ( php -\"dxdebug.mode\"=off -S localhost:8000 )\n");echo"\ngot filesize=" .strlen($get) .PHP_EOL;

CLI commands:

# to start local serverphp -S localhost:8000# to run the testphp test.php

Wrong output (happens irregularly):

> php .\src\test.phpexpected filesize= 1000progress: 0% (0/1000 b) (chunksize=0)progress: 9.9% (99/1000 b) (chunksize=99)progress: 19.8% (198/1000 b) (chunksize=99)progress: 29.7% (297/1000 b) (chunksize=99)progress: 39.6% (396/1000 b) (chunksize=99)progress: 49.5% (495/1000 b) (chunksize=99)progress: 59.4% (594/1000 b) (chunksize=99)progress: 69.3% (693/1000 b) (chunksize=99)progress: 79.2% (792/1000 b) (chunksize=99)progress: 89.1% (891/1000 b) (chunksize=99)progress: 90.1% (901/1000 b) (chunksize=10)got filesize=1000

Correct output:

> php .\src\test.phpexpected filesize= 1000progress: 0% (0/1000 b) (chunksize=0)progress: 9.9% (99/1000 b) (chunksize=99)progress: 19.8% (198/1000 b) (chunksize=99)progress: 29.7% (297/1000 b) (chunksize=99)progress: 39.6% (396/1000 b) (chunksize=99)progress: 49.5% (495/1000 b) (chunksize=99)progress: 59.4% (594/1000 b) (chunksize=99)progress: 69.3% (693/1000 b) (chunksize=99)progress: 79.2% (792/1000 b) (chunksize=99)progress: 89.1% (891/1000 b) (chunksize=99)progress: 99% (990/1000 b) (chunksize=99)progress: 100% (1000/1000 b) (chunksize=10)got filesize=1000

Description

The fileecho.php generates a series of 1000 consecutive"x"s. The data is emitted with intervals of 50ms (usleep(50 * 1000)), in 10 chunks sized 99bytes, plus one final chunk of 10bytes (99*10 + 10 = 1000).

The scripttest.php fetches the data from the url/echo.php of the local web server.file_get_contents is passed a context with anotification callback. This callback prints the download progress: percentage, bytes transferred, total bytes, current chunk size. The chunk size is calculated by subtracting theprevious$bytes_transferred from the current$bytes_transferred.

If you runphp test.php multiple times, you will see thatsometimes the event last 1/2STREAM_NOTIFY_PROGRESS events are not emitted correctly.

The last events shouldalways be:

  • progress: 89.1% (891/1000 b) (chunksize=99) = chunk 9
  • progress: 99% (**990**/1000 b) (chunksize=**99**) = chunk 10
  • progress: 100% (**1000**/1000 b) (chunksize=**10**) = chunk 11

Instead, sometimes the last chunks are messed up:

  • progress: 89.1% (891/1000 b) (chunksize=99) = chunk 9
  • progress: 90.1% (**901**/1000 b) (chunksize=**10**) = chunk 10. This line here is the culprit. This chunk has the correct size of the last chunk (10 bytes), but it's reported as being the 10th! In reality, the 10th chunk is still 99bytes large, and the 11th is the 10bytes one.

When this problem occurs, 100% progress is never emitted (as the 10th chunk is mistaken for the 11th).


I don't think this is a problem in the communication betweenphp and the web server. Any possible delay in the communication shouldn't interfere with how the chunks are passed to the notification callback. I tried runningcurl -#O multiple times to see howcurl receives the data. This is the command line i used (powershell):1..10 | % { curl -#LO http://localhost:8000/echo.php *>&1 | php -r "preg_match_all('/[0-9\.]+/m', stream_get_contents(STDIN), `$m);echo implode(', ', `$m[0]).PHP_EOL;" ;}. For each iteration (1..10), this prints every progress percentage reported bycurl.

It prints something like this:

19.8, 39.6, 49.5, 59.4, 69.3, 79.2, 99.0, 100.019.8, 29.7, 39.6, 49.5, 59.4, 69.3, 79.2, 89.1, 99.0, 100.019.8, 29.7, 39.6, 49.5, 59.4, 69.3, 89.1, 99.0, 100.019.8, 39.6, 49.5, 59.4, 69.3, 79.2, 89.1, 99.0, 100.019.8, 29.7, 39.6, 49.5, 59.4, 69.3, 79.2, 99.0, 100.019.8, 29.7, 39.6, 59.4, 69.3, 79.2, 89.1, 99.0, 100.019.8, 29.7, 39.6, 49.5, 59.4, 79.2, 89.1, 99.0, 100.019.8, 29.7, 39.6, 49.5, 59.4, 69.3, 79.2, 89.1, 99.0, 100.019.8, 39.6, 49.5, 59.4, 69.3, 79.2, 89.1, 99.0, 100.019.8, 29.7, 39.6, 49.5, 59.4, 69.3, 79.2, 89.1, 99.0, 100.0

As you can see,curl doesn't always report its progress, but even though it skips some reporting, it always has the correct progress percentages. It always ends with89.1,99.0,100.0, which is the correct sequence.

php on the other hand sometimes ends with89.1,90.1.


In conclusion, at the moment theSTREAM_NOTIFY_PROGRESS event is not reliable.

I cannot determinewhy this problem occures only sometimes. I really can't. This appearsextremely frequently when the chunks are transferred with varying sizes (eg if the php script on the server callsreadfile(), and/or if the connection to the server is not stable).

PHP Version

PHP 8.1.12

Operating System

Windows 11 (10.0.22621.819)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp