1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the Ssdp project. |
4
|
|
|
* |
5
|
|
|
* @author Daniel Schröder <[email protected]> |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace GravityMedia\Ssdp; |
9
|
|
|
|
10
|
|
|
use Clue\React\Multicast\Factory as MulticastFactory; |
11
|
|
|
use GravityMedia\Ssdp\Options\AliveOptions; |
12
|
|
|
use GravityMedia\Ssdp\Options\ByebyeOptions; |
13
|
|
|
use GravityMedia\Ssdp\Options\DiscoverOptions; |
14
|
|
|
use GravityMedia\Ssdp\Options\UpdateOptions; |
15
|
|
|
use GravityMedia\Ssdp\Request\Factory\AliveFactory; |
16
|
|
|
use GravityMedia\Ssdp\Request\Factory\ByebyeFactory; |
17
|
|
|
use GravityMedia\Ssdp\Request\Factory\DiscoverFactory; |
18
|
|
|
use GravityMedia\Ssdp\Request\Factory\UpdateFactory; |
19
|
|
|
use React\EventLoop\LoopInterface; |
20
|
|
|
use React\Promise\Deferred; |
21
|
|
|
use React\Promise\PromiseInterface; |
22
|
|
|
use Zend\Diactoros\Request\Serializer as RequestSerializer; |
23
|
|
|
use Zend\Diactoros\Response\Serializer as ResponseSerializer; |
24
|
|
|
use Zend\Diactoros\Uri; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Ssdp client class |
28
|
|
|
* |
29
|
|
|
* @package GravityMedia\Ssdp |
30
|
|
|
*/ |
31
|
|
|
class Client |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* The multicast address |
35
|
|
|
*/ |
36
|
|
|
const MULTICAST_ADDRESS = '239.255.255.250'; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* The multicast port |
40
|
|
|
*/ |
41
|
|
|
const MULTICAST_PORT = 1900; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var LoopInterface |
45
|
|
|
*/ |
46
|
|
|
private $loop; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var MulticastFactory |
50
|
|
|
*/ |
51
|
|
|
private $multicastFactory; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var AliveFactory |
55
|
|
|
*/ |
56
|
|
|
protected $aliveRequestFactory; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var ByebyeFactory |
60
|
|
|
*/ |
61
|
|
|
protected $byebyeRequestFactory; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var DiscoverFactory |
65
|
|
|
*/ |
66
|
|
|
protected $discoverRequestFactory; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var UpdateFactory |
70
|
|
|
*/ |
71
|
|
|
protected $updateRequestFactory; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Create client object |
75
|
|
|
* |
76
|
|
|
* @param LoopInterface $loop |
77
|
|
|
*/ |
78
|
4 |
|
public function __construct(LoopInterface $loop) |
79
|
|
|
{ |
80
|
4 |
|
$this->loop = $loop; |
81
|
4 |
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Get multicast factory |
85
|
|
|
* |
86
|
|
|
* @return MulticastFactory |
87
|
|
|
*/ |
88
|
4 |
|
public function getMulticastFactory() |
89
|
|
|
{ |
90
|
4 |
|
if (null === $this->multicastFactory) { |
91
|
2 |
|
$this->multicastFactory = new MulticastFactory($this->loop); |
92
|
1 |
|
} |
93
|
|
|
|
94
|
4 |
|
return $this->multicastFactory; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Set multicast factory |
99
|
|
|
* |
100
|
|
|
* @param MulticastFactory $multicastFactory |
101
|
|
|
* |
102
|
|
|
* @return $this |
103
|
|
|
*/ |
104
|
2 |
|
public function setMulticastFactory(MulticastFactory $multicastFactory) |
105
|
|
|
{ |
106
|
2 |
|
$this->multicastFactory = $multicastFactory; |
107
|
2 |
|
return $this; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Get alive request factory |
112
|
|
|
* |
113
|
|
|
* @return AliveFactory |
114
|
|
|
*/ |
115
|
|
|
public function getAliveRequestFactory() |
116
|
|
|
{ |
117
|
|
|
if (null === $this->aliveRequestFactory) { |
118
|
|
|
$this->aliveRequestFactory = new AliveFactory(); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
return $this->aliveRequestFactory; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Get byebye request factory |
126
|
|
|
* |
127
|
|
|
* @return ByebyeFactory |
128
|
|
|
*/ |
129
|
|
|
public function getByebyeRequestFactory() |
130
|
|
|
{ |
131
|
|
|
if (null === $this->byebyeRequestFactory) { |
132
|
|
|
$this->byebyeRequestFactory = new ByebyeFactory(); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
return $this->byebyeRequestFactory; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Get discover request factory |
140
|
|
|
* |
141
|
|
|
* @return DiscoverFactory |
142
|
|
|
*/ |
143
|
4 |
|
protected function getDiscoverRequestFactory() |
144
|
|
|
{ |
145
|
4 |
|
if (null === $this->discoverRequestFactory) { |
146
|
4 |
|
$this->discoverRequestFactory = new DiscoverFactory(); |
147
|
2 |
|
} |
148
|
|
|
|
149
|
4 |
|
return $this->discoverRequestFactory; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Get update request factory |
154
|
|
|
* |
155
|
|
|
* @return UpdateFactory |
156
|
|
|
*/ |
157
|
|
|
public function getUpdateRequestFactory() |
158
|
|
|
{ |
159
|
|
|
if (null === $this->updateRequestFactory) { |
160
|
|
|
$this->updateRequestFactory = new UpdateFactory(); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
return $this->updateRequestFactory; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Send alive request |
168
|
|
|
* |
169
|
|
|
* @param AliveOptions $options |
170
|
|
|
* |
171
|
|
|
* @return PromiseInterface |
172
|
|
|
*/ |
173
|
|
View Code Duplication |
public function alive(AliveOptions $options) |
|
|
|
|
174
|
|
|
{ |
175
|
|
|
$request = $this->getAliveRequestFactory()->createRequest($options); |
176
|
|
|
$data = trim(RequestSerializer::toString($request)) . "\r\n\r\n"; |
177
|
|
|
|
178
|
|
|
$socket = $this->getMulticastFactory()->createSender(); |
179
|
|
|
|
180
|
|
|
$socket->send($data, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT)); |
181
|
|
|
|
182
|
|
|
return $this; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Send byebye request |
187
|
|
|
* |
188
|
|
|
* @param ByebyeOptions $options |
189
|
|
|
* |
190
|
|
|
* @return PromiseInterface |
191
|
|
|
*/ |
192
|
|
View Code Duplication |
public function byebye(ByebyeOptions $options) |
|
|
|
|
193
|
|
|
{ |
194
|
|
|
$request = $this->getByebyeRequestFactory()->createRequest($options); |
195
|
|
|
$data = trim(RequestSerializer::toString($request)) . "\r\n\r\n"; |
196
|
|
|
|
197
|
|
|
$socket = $this->getMulticastFactory()->createSender(); |
198
|
|
|
|
199
|
|
|
$socket->send($data, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT)); |
200
|
|
|
|
201
|
|
|
return $this; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Send discover request |
206
|
|
|
* |
207
|
|
|
* @param DiscoverOptions $options |
208
|
|
|
* @param int $timeout |
209
|
|
|
* |
210
|
|
|
* @return PromiseInterface |
211
|
|
|
*/ |
212
|
4 |
|
public function discover(DiscoverOptions $options, $timeout = 2) |
213
|
|
|
{ |
214
|
4 |
|
$request = $this->getDiscoverRequestFactory()->createRequest($options); |
215
|
4 |
|
$data = trim(RequestSerializer::toString($request)) . "\r\n\r\n"; |
216
|
|
|
|
217
|
4 |
|
$socket = $this->getMulticastFactory()->createSender(); |
218
|
|
|
|
219
|
|
|
$timer = $this->loop->addTimer($timeout, function () use ($socket, &$deferred) { |
220
|
2 |
|
$deferred->resolve(); |
221
|
2 |
|
$socket->close(); |
222
|
4 |
|
}); |
223
|
|
|
|
224
|
|
|
$deferred = new Deferred(function () use ($socket, &$timer) { |
|
|
|
|
225
|
2 |
|
$timer->cancel(); |
226
|
2 |
|
$socket->close(); |
227
|
2 |
|
throw new \RuntimeException('Discovery cancelled.'); |
228
|
4 |
|
}); |
229
|
|
|
|
230
|
4 |
|
$socket->on('message', function ($message, $remote) use ($deferred) { |
231
|
|
|
$deferred->notify([ |
232
|
|
|
'message' => $this->parseMessage($message), |
233
|
|
|
'remote' => $remote |
234
|
|
|
]); |
235
|
4 |
|
}); |
236
|
|
|
|
237
|
4 |
|
$socket->send($data, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT)); |
238
|
|
|
|
239
|
4 |
|
return $deferred->promise(); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Send update request |
244
|
|
|
* |
245
|
|
|
* @param UpdateOptions $options |
246
|
|
|
* |
247
|
|
|
* @return PromiseInterface |
248
|
|
|
*/ |
249
|
|
View Code Duplication |
public function update(UpdateOptions $options) |
|
|
|
|
250
|
|
|
{ |
251
|
|
|
$request = $this->getUpdateRequestFactory()->createRequest($options); |
252
|
|
|
$data = trim(RequestSerializer::toString($request)) . "\r\n\r\n"; |
253
|
|
|
|
254
|
|
|
$socket = $this->getMulticastFactory()->createSender(); |
255
|
|
|
|
256
|
|
|
$socket->send($data, sprintf('%s:%s', self::MULTICAST_ADDRESS, self::MULTICAST_PORT)); |
257
|
|
|
|
258
|
|
|
return $this; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Parse message |
263
|
|
|
* |
264
|
|
|
* @param string $message |
265
|
|
|
* |
266
|
|
|
* @return Message |
267
|
|
|
*/ |
268
|
|
|
protected function parseMessage($message) |
269
|
|
|
{ |
270
|
|
|
$response = ResponseSerializer::fromString($message); |
271
|
|
|
$message = new Message(); |
272
|
|
|
|
273
|
|
|
if ($response->hasHeader('CACHE-CONTROL')) { |
274
|
|
|
$value = $response->getHeaderLine('CACHE-CONTROL'); |
275
|
|
|
$message->setLifetime(intval(substr($value, strpos($value, '=') + 1))); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
if ($response->hasHeader('DATE')) { |
279
|
|
|
$message->setDate(new \DateTime($response->getHeaderLine('DATE'))); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
if ($response->hasHeader('LOCATION')) { |
283
|
|
|
$message->setDescriptionUrl(new Uri($response->getHeaderLine('LOCATION'))); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
if ($response->hasHeader('SERVER')) { |
287
|
|
|
$message->setServerString($response->getHeaderLine('SERVER')); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
if ($response->hasHeader('ST')) { |
291
|
|
|
$message->setSearchTargetString($response->getHeaderLine('ST')); |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
if ($response->hasHeader('USN')) { |
295
|
|
|
$message->setUniqueServiceNameString($response->getHeaderLine('USN')); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
return $message; |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
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.