Skip to content
This repository has been archived by the owner on Sep 20, 2021. It is now read-only.

Data read from socket doesn't match expected length #95

Open
okdana opened this issue Jun 27, 2017 · 6 comments
Open

Data read from socket doesn't match expected length #95

okdana opened this issue Jun 27, 2017 · 6 comments

Comments

@okdana
Copy link

okdana commented Jun 27, 2017

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();
@Hywan Hywan self-assigned this Jun 28, 2017
@Hywan
Copy link
Member

Hywan commented Jun 28, 2017

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.

@Pierozi
Copy link
Member

Pierozi commented Jul 17, 2017

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

@Pierozi
Copy link
Member

Pierozi commented Jul 17, 2017

This could also explain our issue related to TLS handshake.

@Hywan
Copy link
Member

Hywan commented Aug 7, 2017

Do you have a clue on this one @Pierozi?

@Pierozi
Copy link
Member

Pierozi commented Aug 20, 2017

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
Copy link
Member

Pierozi commented Oct 22, 2017

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Development

No branches or pull requests

3 participants