react-http2/src/Frame/Frame.php

126 lines
4.8 KiB
PHP

<?php
namespace NoccyLabs\React\Http2\Frame;
abstract class Frame
{
const FRAME_DATA = 0x0;
const FRAME_HEADERS = 0x1;
const FRAME_PRIORITY = 0x2;
const FRAME_SETTINGS = 0x4;
const FRAME_PUSH_PROMISE = 0x5;
const FRAME_PING = 0x6;
const FRAME_GOAWAY = 0x7;
const FRAME_WINDOW_UPDATE = 0x8;
const FRAME_CONTINUATION = 0x9;
public static $frameMap = [
self::FRAME_DATA => DataFrame::class,
self::FRAME_HEADERS => HeadersFrame::class,
self::FRAME_PRIORITY => PriorityFrame::class,
self::FRAME_SETTINGS => SettingsFrame::class,
self::FRAME_PUSH_PROMISE => PushPromiseFrame::class,
self::FRAME_PING => PingFrame::class,
self::FRAME_GOAWAY => GoAwayFrame::class,
self::FRAME_WINDOW_UPDATE => WindowUpdateFrame::class,
self::FRAME_CONTINUATION => ContinuationFrame::class
];
/** @var int The associated condition is not a result of an error. For example, a GOAWAY might include this code to indicate graceful shutdown of a connection. */
const ERROR_NO_ERROR = 0x0;
/** @var int The endpoint detected an unspecific protocol error. This error is for use when a more specific error code is not available. */
const ERROR_PROTOCOL_ERROR = 0x1;
/** @var int The endpoint encountered an unexpected internal error. */
const ERROR_INTERNAL_ERROR = 0x2;
/** @var int The endpoint detected that its peer violated the flow-control protocol. */
const ERROR_FLOW_CONTROL_ERROR = 0x3;
/** @var int The endpoint sent a SETTINGS frame but did not receive a response in a timely manner. See Section 6.5.3 ("Settings Synchronization"). */
const ERROR_SETTINGS_TIMEOUT = 0x4;
/** @var int The endpoint received a frame after a stream was half-closed. */
const ERROR_STREAM_CLOSED = 0x5;
/** @var int The endpoint received a frame with an invalid size. */
const ERROR_FRAME_SIZE_ERROR = 0x6;
/** @var int The endpoint refused the stream prior to performing any application processing (see Section 8.1.4 for details). */
const ERROR_REFUSED_STREAM = 0x7;
/** @var int Used by the endpoint to indicate that the stream is no longer needed. */
const ERROR_CANCEL = 0x8;
/** @var int The endpoint is unable to maintain the header compression context for the connection. */
const ERROR_COMPRESSION_ERROR = 0x9;
/** @var int The connection established in response to aCONNECT request (Section 8.3) was reset or abnormally closed. */
const ERROR_CONNECT_ERROR = 0xa;
/** @var int The endpoint detected that its peer is exhibiting a behavior that might be generating excessive load. */
const ERROR_ENHANCE_YOUR_CALM = 0xb;
/** @var int The underlying transport has properties that do not meet minimum security requirements (see Section 9.2). */
const ERROR_INADEQUATE_SECURITY = 0xc;
/** @var int The endpoint requires that HTTP/1.1 be used instead of HTTP/2. */
const ERROR_HTTP_1_1_REQUIRED = 0xd;
protected int $frameType;
protected int $frameFlags;
protected int $streamIdentifier;
public function getFrameType(): int
{
return $this->frameType;
}
abstract public function toBinary(): string;
abstract public function fromBinary(string $binary): void;
public function encodeFrame(): string
{
$frame = '';
// TODO build header
$payload = $this->toBinary();
$length = strlen($payload);
$frame .= chr(($length >> 16) & 0xFF)
. chr(($length >> 8) & 0xFF)
. chr(($length) & 0xFF)
. chr($this->frameType)
. chr($this->frameFlags)
. chr(($this->streamIdentifier >> 24) & 0x7F)
. chr(($this->streamIdentifier >> 16) & 0xFF)
. chr(($this->streamIdentifier >> 8) & 0xFF)
. chr(($this->streamIdentifier) & 0xFF)
. $payload;
return $frame;
}
public static function parseFrame(string &$data): Frame
{
$header = array_map('ord', str_split(substr($data,0,9)));
$length = ($header[0] << 16)
| ($header[1] << 8)
| ($header[2]);
$type = $header[3];
$flags = $header[4];
$stream = (($header[5] & 0x7F) << 24)
| ($header[6] << 16)
| ($header[7] << 8)
| ($header[8]);
if (!array_key_exists($type, self::$frameMap)) {
// TODO handle this
}
/** @var Frame $frame */
$frame = new self::$frameMap[$type];
$frame->frameType = $type;
$frame->frameFlags = $flags;
$frame->streamIdentifier = $stream;
// Grab the payload and parse it
$payload = substr($data, 9, $length);
$frame->fromBinary($payload);
return $frame;
}
}