1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
namespace Hyphper\Frame; |
4
|
|
|
|
5
|
|
|
use Hyphper\Frame\Exception\InvalidFrameException; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* The SETTINGS frame conveys configuration parameters that affect how |
9
|
|
|
* endpoints communicate. The parameters are either constraints on peer |
10
|
|
|
* behavior or preferences. |
11
|
|
|
* |
12
|
|
|
* Settings are not negotiated. Settings describe characteristics of the |
13
|
|
|
* sending peer, which are used by the receiving peer. Different values for |
14
|
|
|
* the same setting can be advertised by each peer. For example, a client |
15
|
|
|
* might set a high initial flow control window, whereas a server might set a |
16
|
|
|
* lower value to conserve resources. |
17
|
|
|
* |
18
|
|
|
* @package Hyphper\Frame |
19
|
|
|
*/ |
20
|
|
|
class SettingsFrame extends \Hyphper\Frame |
21
|
|
|
{ |
22
|
|
|
protected $defined_flags = [Flag::ACK]; |
23
|
|
|
protected $type = 0x04; |
24
|
|
|
protected $stream_association = self::NO_STREAM; |
25
|
|
|
protected $settings; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting. |
29
|
|
|
*/ |
30
|
|
|
const HEADER_TABLE_SIZE = 0x01; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* The byte that signals the SETTINGS_ENABLE_PUSH setting. |
34
|
|
|
*/ |
35
|
|
|
const ENABLE_PUSH = 0x02; |
36
|
|
|
/** |
37
|
|
|
* The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting. |
38
|
|
|
*/ |
39
|
|
|
const MAX_CONCURRENT_STREAMS = 0x03; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting. |
43
|
|
|
*/ |
44
|
|
|
const INITIAL_WINDOW_SIZE = 0x04; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The byte that signals the SETTINGS_MAX_FRAME_SIZE setting. |
48
|
|
|
*/ |
49
|
|
|
const MAX_FRAME_SIZE = 0x05; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting. |
53
|
|
|
*/ |
54
|
|
|
const MAX_HEADER_LIST_SIZE = 0x06; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* SettingsFrame constructor. |
58
|
|
|
* |
59
|
|
|
* @param array $options |
60
|
|
|
* @throws InvalidFrameException |
61
|
|
|
*/ |
62
|
9 |
|
public function __construct(array $options = []) // array $settings = [], ... $args) |
63
|
|
|
{ |
64
|
9 |
|
parent::__construct($options); |
65
|
|
|
|
66
|
8 |
|
$options['settings'] = $options['settings'] ?? []; |
67
|
|
|
|
68
|
8 |
|
if ($options['settings'] && $this->flags->hasFlag(Flag::ACK)) { |
69
|
1 |
|
throw new InvalidFrameException('Settings must be empty if ACK flag is set.'); |
70
|
|
|
} |
71
|
|
|
|
72
|
7 |
|
$this->settings = $options['settings']; |
73
|
7 |
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @return string |
77
|
|
|
*/ |
78
|
1 |
|
public function serializeBody(): string |
79
|
|
|
{ |
80
|
1 |
|
$settings = []; |
81
|
1 |
|
foreach ($this->settings as $setting => $value) { |
82
|
1 |
|
$settings[] = pack('nN', $setting & 0xFF, $value); |
83
|
|
|
} |
84
|
|
|
|
85
|
1 |
|
return implode('', $settings); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Given the body of a frame, parses it into frame data. This populates |
90
|
|
|
* the non-header parts of the frame: that is, it does not populate the |
91
|
|
|
* stream ID or flags. |
92
|
|
|
* |
93
|
|
|
* |
94
|
|
|
* @param string $data |
95
|
|
|
* @throws InvalidFrameException |
96
|
|
|
* @return void |
97
|
|
|
*/ |
98
|
2 |
|
public function parseBody(string $data) |
99
|
|
|
{ |
100
|
2 |
|
foreach (range(0, strlen($data) - 1, 6) as $i) { |
101
|
2 |
View Code Duplication |
if (!$unpack = @unpack('nname/Nvalue', substr($data, $i, $i + 6))) { |
|
|
|
|
102
|
1 |
|
throw new InvalidFrameException('Invalid SETTINGS body'); |
103
|
|
|
} |
104
|
|
|
|
105
|
2 |
|
$name = $unpack['name']; |
106
|
2 |
|
$value = $unpack['value']; |
107
|
2 |
|
$this->settings[$name] = $value; |
108
|
|
|
} |
109
|
|
|
|
110
|
1 |
|
$this->body_len = strlen($data); |
111
|
1 |
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @return array|mixed |
115
|
|
|
*/ |
116
|
3 |
|
public function getSettings() |
117
|
|
|
{ |
118
|
3 |
|
return $this->settings; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param array|mixed $settings |
123
|
|
|
* @return SettingsFrame |
124
|
|
|
*/ |
125
|
1 |
|
public function setSettings($settings) |
126
|
|
|
{ |
127
|
1 |
|
$this->settings = $settings; |
128
|
1 |
|
return $this; |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.