Websocket icon indicating copy to clipboard operation
Websocket copied to clipboard

Data read from socket doesn't match expected length

Open okdana opened this issue 8 years ago • 6 comments

Hello!

I've been using Hoa\Websocket to interface with Chromium's debugging protocol in order to print PDFs. Chromium returns the PDF data in the response as a base64-encoded string, which can grow quite long (several MiB). I found that the response that i was getting back was the wrong size, usually severely truncated (tens of KiB).

To rule out an issue with Chromium, i created a server.php that simply sends 5 MB of base64-encoded garbage back to the client on connect, and i made a client.php to just print the length of the bucket message it receives. This is my result:

~ % php server.php &
[1] 5224
~ % php client.php
server: Sending 5000000 bytes
client: Received 22390 bytes
~ % php client.php
server: Sending 5000000 bytes
client: Received 22390 bytes
~ % php client.php
server: Sending 5000000 bytes
client: Received 22390 bytes
~ % php client.php
server: Sending 5000000 bytes
client: Received 1749065 bytes
~ % php client.php
server: Sending 5000000 bytes
client: Received 789220 bytes
~ % php client.php
server: Sending 5000000 bytes
client: Received 789220 bytes
~ % php client.php
server: Sending 5000000 bytes
client: Received 4411468 bytes

Ultimately the issue seems to lie in the call to stream_socket_recvfrom() within Hoa\Socket\Connection\Connection::read(). I've confirmed that it receives the correct $length, but the result never matches it.

I'm creating the issue here rather than under Hoa\Socket because i'm assuming this has something to do with blocking or buffering settings rather than any flaw in the read() method itself... but i'm not sure. Am i doing something wrong?

Code to replicate:

server.php

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Hoa\Event;
use Hoa\Socket;
use Hoa\Websocket;

$bytes  = $argv[1] ?? 5000000;
$server = new Websocket\Server(new Socket\Server('ws://127.0.0.1:8889'));

$server->on('open', function (Event\Bucket $bucket) use ($bytes) {
    $data = substr(base64_encode(random_bytes($bytes)), 0, $bytes);
    printf("server: Sending %d bytes\n", strlen($data));
    $bucket->getSource()->send($data);
});

$server->run();

client.php

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Hoa\Event;
use Hoa\Socket;
use Hoa\Websocket;

$client = new Websocket\Client(new Socket\Client('ws://127.0.0.1:8889'));

$client->on('message', function (Event\Bucket $bucket) {
    printf("client: Received %d bytes\n", strlen($bucket->getData()['message']));
});

$client->setHost('127.0.0.1');
$client->connect();
$client->receive();

okdana avatar Jun 27 '17 21:06 okdana

Hello, and thank you for your exemplary bug report 👏! This is all clear.

I can reproduce the bug.

I have added some debug instructions directly into Hoa\Socket\Connection\Connection into the read and write methods. Here is what I have (comments with // represent my annotations)

From the server:

$ php server.php
expect to read 2048, received 208    // read the HTTP request —handshake starts—
expect to write 156, write 156       // write the HTTP response —handshake ends—
Sending 500000 bytes                 // sending the payload to the client
expect to write 500010, write 500010 // inside the `write` method, just around `stream_socket_sendto`
expect to read 1, received 0         // close request from the client
expect to write 4, write 4           // close response to the client

From the client:

$ php client.php
expect to read 2048, received 156        // read the HTP response —handshake ends—
expect to read 1, received 1             // read the frame —first part: fin, opcode etc.—
expect to read 1, received 1             // read the frame —second part: mask (`0x0`) and length (`0x7f`)—
expect to read 8, received 8             // length is incomplete, too large, so read 8 bytes to get the correct message length
expect to read 500000, received 391958   // inside `Hoa\Socket\Connection\Connection::read`,
                                         // `stream_socket_recvfrom` is asked to read 500000 bytes, but it receives only 391958
Received 391958 bytes

So, I guess stream_socket_recvfrom does not read the whole message. This is an issue with hoa/socket. Maybe @Pierozi can help on this one. Let's keep this issue opened here for now.

Hywan avatar Jun 28 '17 06:06 Hywan

Hello @okdana, thanks for this great report, I will work on it asap.

Pierozi avatar Jul 17 '17 12:07 Pierozi

This could also explain our issue related to TLS handshake.

Pierozi avatar Jul 17 '17 12:07 Pierozi

Do you have a clue on this one @Pierozi?

Hywan avatar Aug 07 '17 08:08 Hywan

Yes i've made some fix test. The max stream buffer size depend of the system and method used. We have to loop until the buffer is empty or the expected length is extracted.

We have to deal with encrypted and unencrypted mode, because fread is limited to 8192Bytes. We could also set stream_set_read_buffer to 0 to unbuffered. But a stream without buffer does not sound great.

I will submit a PR soon.

Pierozi avatar Aug 20 '17 10:08 Pierozi

Short update here, we have two possibilities to solve this issue, depending of how the PR 53 on \Hoa\Socket will be implemented.

Pierozi avatar Oct 22 '17 13:10 Pierozi