Repository navigation
Expand file tree
/
Copy pathDockerRawStream.php
More file actions
130 lines (103 loc) · 3.02 KB
/
Copy pathDockerRawStream.php
File metadata and controls
130 lines (103 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php
declare(strict_types=1);
namespace Docker\Stream;
use Psr\Http\Message\StreamInterface;
class DockerRawStream
{
public const HEADER = 'application/vnd.docker.raw-stream';
public const MULTIPLEXED_HEADER = 'application/vnd.docker.multiplexed-stream';
/** @var StreamInterface Stream for the response */
protected $stream;
private bool $multiplexed;
/** @var callable[] A list of callable to call when there is a stdin output */
protected $onStdinCallables = [];
/** @var callable[] A list of callable to call when there is a stdout output */
protected $onStdoutCallables = [];
/** @var callable[] A list of callable to call when there is a stderr output */
protected $onStderrCallables = [];
public function __construct(StreamInterface $stream, bool $multiplexed = true)
{
$this->stream = $stream;
$this->multiplexed = $multiplexed;
}
/**
* Add a callable to read stdin.
*/
public function onStdin(callable $callback): void
{
$this->onStdinCallables[] = $callback;
}
/**
* Add a callable to read stdout.
*/
public function onStdout(callable $callback): void
{
$this->onStdoutCallables[] = $callback;
}
/**
* Add a callable to read stderr.
*/
public function onStderr(callable $callback): void
{
$this->onStderrCallables[] = $callback;
}
/**
* Read a frame in the stream.
*/
protected function readFrame(): void
{
if (!$this->multiplexed) {
$output = $this->stream->read(8192);
if ('' !== $output) {
foreach ($this->onStdoutCallables as $callback) {
$callback($output);
}
}
return;
}
$header = $this->forceRead(8);
if (\strlen($header) < 8) {
return;
}
$decoded = unpack('C1type/C3/N1size', $header);
$output = $this->forceRead($decoded['size']);
$callbackList = [];
if (0 === $decoded['type']) {
$callbackList = $this->onStdinCallables;
}
if (1 === $decoded['type']) {
$callbackList = $this->onStdoutCallables;
}
if (2 === $decoded['type']) {
$callbackList = $this->onStderrCallables;
}
foreach ($callbackList as $callback) {
$callback($output);
}
}
/**
* Force to have something of the expected size (block).
*
* @return string
*/
private function forceRead($length)
{
if (0 === $length) {
return '';
}
$read = '';
do {
$read .= $this->stream->read($length - \strlen($read));
} while (\strlen($read) < $length && !$this->stream->eof());
return $read;
}
/**
* Wait for stream to finish and call callables if defined.
*/
public function wait(): void
{
while (!$this->stream->eof()) {
$this->readFrame();
}
}
}