🕌
【PHP】TLS なしの HTTP/2 サーバーにフレームを送信する
Go で書かれた TLS なしの HTTP/2 サーバーにフレームをいくつか送信してみた。
ログをみるかぎりそれっぽい応答が得られている。
> GODEBUG=http2debug=2,http1debug=2 go run main.go
Listening [0.0.0.0:8000]...
2024/07/05 12:40:42 h2c: attempting h2c with prior knowledge.
2024/07/05 12:40:42 http2: server connection from [::1]:58570 on 0xc0000fc000
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: wrote SETTINGS len=30, settings: MAX_FRAME_SIZE=1048576, MAX_CONCURRENT_STREAMS=250, MAX_HEADER_LIST_SIZE=1048896, HEADER_TABLE_SIZE=4096, INITIAL_WINDOW_SIZE=1048576
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: read SETTINGS len=0
2024/07/05 12:40:42 http2: server read frame SETTINGS len=0
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: read SETTINGS flags=ACK len=0
2024/07/05 12:40:42 http2: server read frame SETTINGS flags=ACK len=0
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: read HEADERS flags=END_STREAM|END_HEADERS stream=1 len=61
2024/07/05 12:40:42 http2: decoded hpack field header field ":method" = "GET"
2024/07/05 12:40:42 http2: decoded hpack field header field ":path" = "/"
2024/07/05 12:40:42 http2: decoded hpack field header field ":scheme" = "https"
2024/07/05 12:40:42 http2: decoded hpack field header field ":authority" = "nghttp2.org"
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: wrote SETTINGS flags=ACK len=0
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: wrote WINDOW_UPDATE len=4 (conn) incr=983041
2024/07/05 12:40:42 http2: server read frame HEADERS flags=END_STREAM|END_HEADERS stream=1 len=61
2024/07/05 12:40:42 http2: server connection error from [::1]:58570: connection error: FRAME_SIZE_ERROR
2024/07/05 12:40:42 http2: Framer 0xc0001840e0: wrote GOAWAY len=8 LastStreamID=1 ErrCode=FRAME_SIZE_ERROR Debug=""
2024/07/05 12:40:43 GOAWAY close timer fired; closing conn from [::1]:58570
1回の connect
ですべてのリクエストを送信したのでdata
イベントの段階で複数の段階のレスポンスがまとまった状態になってしまった。また複数回リクエストすると得られるレスポンスが代わってしまうことがある。
php client.php
00001e0400000000000005001000000003000000fa000600100140000100001000000400100000
39
php client.php
00001e0400000000000005001000000003000000fa000600100140000100001000000400100000000000040100000000000004080000000000000f0001
61
client.php
<?php
require __DIR__ . '/vendor/autoload.php';
$uri = "tcp://localhost:8000";
$connector = new React\Socket\Connector();
$connector->connect($uri)->then(function (React\Socket\ConnectionInterface $connection) {
$connection->on('connection', function () use ($connection) {
echo "connection\r\n";
});
$connection->on('data', function ($chunk) use ($connection) {
echo bin2hex($chunk), PHP_EOL;
echo strlen($chunk), PHP_EOL;
$connection->end();
});
// https://qiita.com/0xfffffff7/items/0960527dca6de364daeb
//
// HTTP/2 コネクションプリフェイス
$connection->write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
// Setting フレーム 0x04
$settings = "\x00\x00\x00".
"\x04".
"\x00".
"\x00\x00\x00\x00";
$connection->write($settings);
// ACK 0x04
$ack = "\x00\x00\x00".
"\x04".
"\x01".
"\x00\x00\x00\x00";
$connection->write($ack);
// HEADERS フレーム 0x01
$headers = "\x00\x00\x3d\x01\x05\x00\x00\x00\x01". // ヘッダフレーム
"\x00". // 圧縮情報
"\x07\x3a\x6d\x65\x74\x68\x6f\x64". // 7 :method
"\x03\x47\x45\x54". // 3 GET
"\x00". // 圧縮情報
"\x05\x3a\x70\x61\x74\x68". // 5 :path
"\x01\x2f". // 1 /
"\x00". // 圧縮情報
"\x07\x3a\x73\x63\x68\x65\x6d\x65". // 7 :scheme
"\x05\x68\x74\x74\x70\x73". // 5 https
"\x00". // 圧縮情報
"\x0a\x3a\x61\x75\x74\x68\x6f\x72\x69\x74\x79". // 10 :authority
"\x0b\x6e\x67\x68\x74\x74\x70\x32\x2e\x6f\x72\x67"; // 11 nghttp2.org
$connection->write($headers);
// GOAWAY フレーム 0x07
$away = "\x00\x00\x00".
"\x07".
"\x00".
"\x00\x00\x00\x00".
"\x00\x00\x00\x01\x00\x00\x00\x00";
$connection->write($away);
}, function (Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
});
Discussion