126 lines
4.8 KiB
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;
|
|
}
|
|
|
|
}
|
|
|