Skip to content

Commit a66d5a7

Browse files
committed
Initial commit
0 parents  commit a66d5a7

6 files changed

Lines changed: 198 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor
2+
/composer.lock

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Christian Lück
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is furnished
10+
to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# clue/ssdp-react [![Build Status](https://travis-ci.org/clue/php-ssdp-react.svg?branch=master)](https://travis-ci.org/clue/php-ssdp-react)
2+
3+
Async [Simple Service Discovery Protocol (SSDP)](http://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol), built on top of [React PHP](http://reactphp.org/).
4+
5+
As used in [Univeral Plug and Play](http://de.wikipedia.org/wiki/Universal_Plug_and_Play) (UPnP).
6+
Commonly used by multimedia devices in home networks etc.
7+
8+
See [UPnP device architecture definition](http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf) (PDF).
9+
Uses Multicast and Unicast UDP HTTP messages (HTTPMU/HTTPU),
10+
expired IETF draft: https://tools.ietf.org/html/draft-goland-http-udp-01
11+
12+
This is an alternative to DNS-Based Service Discovery (DNS-SD)
13+
as defined in [RFC 6763](http://tools.ietf.org/html/rfc6763).
14+
15+
> Note: This project is in early alpha stage! Feel free to report any issues you encounter.
16+
17+
## Quickstart example
18+
19+
Once [installed](#install), you can use the following code to search all available UPnP devices in your network:
20+
21+
```php
22+
$loop = React\EventLoop\Factory::create();
23+
$client = new Client($loop);
24+
25+
$client->search()->then(
26+
function () {
27+
echo 'Search completed' . PHP_EOL;
28+
},
29+
function($e) {
30+
echo 'There was an error searching for devices: ' . $e . PHP_EOL;
31+
},
32+
function ($progress) {
33+
echo 'Found a device: ' . PHP_EOL;
34+
var_dump($progress);
35+
echo PHP_EOL;
36+
}
37+
);
38+
39+
$loop->run();
40+
```
41+
42+
See also the [examples](examples).
43+
44+
## Install
45+
46+
The recommended way to install this library is [through composer](http://getcomposer.org). [New to composer?](http://getcomposer.org/doc/00-intro.md)
47+
48+
```JSON
49+
{
50+
"require": {
51+
"clue/ssdp-react": "dev-master"
52+
}
53+
}
54+
```
55+
56+
## License
57+
58+
MIT

composer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "clue/ssdp-react",
3+
"description": "Async Simple Service Discovery Protocol (SSDP), part of Univeral Plug and Play (UPnP)",
4+
"keywords": ["Simple Service Discovery Protocol", "SSDP", "Univeral Plug and Play", "UPnP", "ReactPHP", "async"],
5+
"homepage": "https://github.com/clue/php-ssdp-react",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Christian Lück",
10+
"email": "christian@lueck.tv"
11+
}
12+
],
13+
"autoload": {
14+
"psr-4": { "Clue\\React\\Ssdp\\": "src/" }
15+
},
16+
"require": {
17+
"php": ">=5.3",
18+
"react/event-loop": "~0.4.0|~0.3.0",
19+
"react/promise": "~2.0|~1.0",
20+
"clue/multicast-react": "~0.2.0"
21+
}
22+
}

examples/demo.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
use Clue\React\Ssdp\Client;
4+
5+
require __DIR__ . '/../vendor/autoload.php';
6+
7+
$loop = React\EventLoop\Factory::create();
8+
$client = new Client($loop);
9+
10+
$client->search()->then(
11+
function () {
12+
echo 'Search completed' . PHP_EOL;
13+
},
14+
function($e) {
15+
echo 'There was an error searching for devices: ' . $e . PHP_EOL;
16+
},
17+
function ($progress) {
18+
echo 'Found a device: ' . PHP_EOL;
19+
var_dump($progress);
20+
echo PHP_EOL;
21+
}
22+
);
23+
24+
$loop->run();

src/Client.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace Clue\React\Ssdp;
4+
5+
use React\EventLoop\LoopInterface;
6+
use Clue\React\Multicast\Factory as MulticastFactory;
7+
use React\Promise\Deferred;
8+
9+
class Client
10+
{
11+
const ADDRESS = '239.255.255.250:1900';
12+
13+
private $loop;
14+
private $multicast;
15+
16+
public function __construct(LoopInterface $loop, MulticastFactory $multicast = null)
17+
{
18+
if ($multicast === null) {
19+
$multicast = new MulticastFactory($loop);
20+
}
21+
$this->loop = $loop;
22+
$this->multicast = $multicast;
23+
}
24+
25+
public function search($searchTarget = 'ssdp:all', $mx = 2)
26+
{
27+
$data = "M-SEARCH * HTTP/1.1\r\n";
28+
$data .= "HOST: " . self::ADDRESS . "\r\n";
29+
$data .= "MAN: \"ssdp:discover\"\r\n";
30+
$data .= "MX: $mx\r\n";
31+
$data .= "ST: $searchTarget\r\n";
32+
$data .= "\r\n";
33+
34+
$socket = $this->multicast->createSender();
35+
// TODO: The TTL for the IP packet SHOULD default to 2 and SHOULD be configurable.
36+
37+
$timer = $this->loop->addTimer($mx, function() use ($socket, &$deferred) {
38+
$deferred->resolve();
39+
$socket->close();
40+
});
41+
42+
$deferred = new Deferred(function () use ($socket, &$timer) {
43+
// canceling resulting promise cancels timer and closes socket
44+
$timer->cancel();
45+
$socket->close();
46+
throw new RuntimeException('Cancelled');
47+
});
48+
49+
$that = $this;
50+
$socket->on('message', function ($data, $remote) use ($deferred, $that) {
51+
$message = $that->parseMessage($data, $remote);
52+
53+
$deferred->progress($message);
54+
});
55+
56+
$socket->send($data, self::ADDRESS);
57+
58+
return $deferred->promise();
59+
}
60+
61+
/** @internal */
62+
public function parseMessage($message, $remote)
63+
{
64+
// TODO: parse message into message model
65+
$message = array(
66+
'data' => $message,
67+
'_sender' => $remote
68+
);
69+
return $message;
70+
}
71+
}

0 commit comments

Comments
 (0)