🌟

【PHP】Revolt で HTTP/2 フレームを送信する

2024/07/07に公開

PRI の送信だけはうまくいったが、Settings フレームを一緒に送信するとハングしてしまう

> php client.php

array(2) {
  ["size"]=>
  int(39)
  ["chunk"]=>
  string(78) "00001e0400000000000005001000000003000000fa000600100140000100001000000400100000"
}
array(2) {
  ["size"]=>
  int(13)
  ["chunk"]=>
  string(26) "000004080000000000000f0001"
}
client.php
// https://github.com/revoltphp/event-loop/blob/main/examples/http-client-async.php

use Revolt\EventLoop;

require __DIR__ . '/vendor/autoload.php';

// https://gist.github.com/masakielastic/8840124754b38663373f8f74d6f162d4
require __DIR__.'/h2frames.php';

# h2c テストサーバー
# https://zenn.dev/masakielastic/articles/530f9751697ed6

$stream = \stream_socket_client(
  'tcp://localhost:8000', $errno, $errstr,
  PHP_INT_MAX, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT
);
stream_set_blocking($stream, false);

EventLoop::onWritable($stream, function ($watcher, $stream) {
    EventLoop::cancel($watcher);

    fwrite($stream, frames('pri'));

    EventLoop::onReadable($stream, function ($watcher, $stream) {
        $streamId = (int) $stream;
        $chunk = fread($stream, 128);

        if ($chunk === '') {
            EventLoop::cancel($watcher);
            fclose($stream);
            return;
        }

        dump($chunk);
        EventLoop::cancel($streamId);
    });
});

EventLoop::run();

strace php client.php を実行すると pselect6 の実行で止まっていることが示される

read(5, "<?php\n\ndeclare(strict_types=1);\n"..., 1804) = 1804
close(5)                                = 0
pselect6(5, [4], [], [], {tv_sec=0, tv_nsec=0}, NULL) = 0 (Timeout)
pselect6(5, [4], [], [], NULL, NULL

ブロッキングな方法でも同じようにハングしてしまう

client2.php
// https://github.com/revoltphp/event-loop/blob/358572ca31647df7dbe930f13c8567136beffa33/examples/http-client-blocking.php

use Revolt\EventLoop;

require __DIR__ . '/vendor/autoload.php';
// https://gist.github.com/masakielastic/8840124754b38663373f8f74d6f162d4
require __DIR__.'/h2frames.php';

$stream = stream_socket_client('tcp://localhost:8000');
stream_set_blocking($stream, false);

fwrite($stream, h2frames('pri'));
fwrite($stream, h2frames('settings'));

$callbackId = EventLoop::onReadable($stream, function ($watcher, $stream) {
    $chunk = fread($stream, 256);

    if ($chunk === '') {
        echo '[END]' . PHP_EOL;
        EventLoop::cancel($watcher);
        fclose($stream);
        return;
    }

    dump($chunk, 'pri');
});

EventLoop::run();
EventLoop::disable($callbackId);

Discussion