diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d446461..4becf52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ jobs: - ubuntu-latest strategy: matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['8.1', '8.2', '8.3'] steps: - name: Configure Git if: ${{ matrix.os == 'windows-latest' }} diff --git a/composer.json b/composer.json index 69a036a..7cfe1f5 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ "johnstevenson/json-works": "~1.1", "firebase/php-jwt": "^6.0", "guzzlehttp/guzzle": "~6.0|~7.0", - "ext-json": "*" + "ext-json": "*", + "vonage/jwt": "^0.5.1" }, "require-dev": { "phpunit/phpunit": "^7.4|^8.0", @@ -55,5 +56,10 @@ "OpenTok\\": "src/OpenTok", "OpenTokTest\\": "tests/OpenTokTest" } + }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } } } diff --git a/sample/Archiving/README.md b/sample/Archiving/README.md index a866579..d4b6d00 100644 --- a/sample/Archiving/README.md +++ b/sample/Archiving/README.md @@ -56,7 +56,7 @@ $app->get('/host', function () use ($app, $sessionId) { $token = $app->opentok->generateToken($sessionId, array( 'role' => Role::MODERATOR - )); + ), true); $app->render('host.html', array( 'apiKey' => $app->apiKey, diff --git a/src/OpenTok/OpenTok.php b/src/OpenTok/OpenTok.php index bdc8967..36d56f1 100644 --- a/src/OpenTok/OpenTok.php +++ b/src/OpenTok/OpenTok.php @@ -2,10 +2,20 @@ namespace OpenTok; +use DateTimeImmutable; +use Firebase\JWT\Key; +use Lcobucci\JWT\Configuration; +use Lcobucci\JWT\Encoding\ChainedFormatter; +use Lcobucci\JWT\Encoding\JoseEncoder; +use Lcobucci\JWT\Signer\Key\InMemory; +use Lcobucci\JWT\Signer\Rsa\Sha256; +use Lcobucci\JWT\Token\Builder; use OpenTok\Util\Client; use OpenTok\Util\Validators; use OpenTok\Exception\InvalidArgumentException; use OpenTok\Exception\UnexpectedValueException; +use Ramsey\Uuid\Uuid; +use Vonage\JWT\TokenGenerator; /** * Contains methods for creating OpenTok sessions, generating tokens, and working with archives. @@ -19,7 +29,6 @@ */ class OpenTok { - /** @internal */ private $apiKey; /** @internal */ @@ -104,11 +113,56 @@ public function __construct($apiKey, $apiSecret, $options = array()) * * * + * @param bool $legacy By default, OpenTok uses SHA256 JWTs for authentication. Switching + * legacy to true will create a deprecated T1 token for backwards compatibility. + * * @return string The token string. */ - public function generateToken($sessionId, $options = array()) + public function generateToken(string $sessionId, array $options = array(), bool $legacy = false): string + { + if ($legacy) { + return $this->returnLegacyToken($sessionId, $options); + } + + $issuedAt = new \DateTimeImmutable('@' . time()); + + $defaults = [ + 'session_id' => $sessionId, + 'role' => Role::PUBLISHER, + 'expireTime' => null, + 'initial_layout_list' => [''], + 'ist' => 'project', + 'nonce' => mt_rand(), + 'scope' => 'session.connect' + ]; + + $options = array_merge($defaults, array_intersect_key($options, $defaults)); + + $builder = new Builder(new JoseEncoder(), ChainedFormatter::default()); + $builder = $builder->issuedBy($this->apiKey); + + if ($options['expireTime']) { + $expiry = new \DateTimeImmutable('@' . $options['expireTime']); + $builder = $builder->expiresAt($expiry); + } + + unset($options['expireTime']); + + $builder = $builder->issuedAt($issuedAt); + $builder = $builder->canOnlyBeUsedAfter($issuedAt); + $builder = $builder->identifiedBy(bin2hex(random_bytes(16))); + + foreach ($options as $key => $value) { + $builder = $builder->withClaim($key, $value); + } + + $token = $builder->getToken(new \Lcobucci\JWT\Signer\Hmac\Sha256(), InMemory::plainText($this->apiSecret)); + + return $token->toString(); + } + + private function returnLegacyToken(string $sessionId, array $options = []): string { - // unpack optional arguments (merging with default values) into named variables $defaults = array( 'role' => Role::PUBLISHER, 'expireTime' => null, @@ -237,7 +291,6 @@ public function createSession($options = array()) } if (array_key_exists('e2ee', $options) && $options['e2ee']) { - if (array_key_exists('mediaMode', $options) && $options['mediaMode'] !== MediaMode::ROUTED) { throw new InvalidArgumentException('MediaMode must be routed in order to enable E2EE'); } @@ -885,13 +938,13 @@ public function startBroadcast(string $sessionId, array $options = []): Broadcas Validators::validateResolution($options['resolution']); } - if (isset($options['outputs']['hls'])) { - Validators::validateBroadcastOutputOptions($options['outputs']['hls']); - } + if (isset($options['outputs']['hls'])) { + Validators::validateBroadcastOutputOptions($options['outputs']['hls']); + } - if (isset($options['outputs']['rtmp'])) { - Validators::validateRtmpStreams($options['outputs']['rtmp']); - } + if (isset($options['outputs']['rtmp'])) { + Validators::validateRtmpStreams($options['outputs']['rtmp']); + } $defaults = [ 'layout' => Layout::getBestFit(), @@ -900,11 +953,11 @@ public function startBroadcast(string $sessionId, array $options = []): Broadcas 'streamMode' => 'auto', 'resolution' => '640x480', 'maxBitRate' => 2000000, - 'outputs' => [ - 'hls' => [ - 'dvr' => false, - 'lowLatency' => false - ] + 'outputs' => [ + 'hls' => [ + 'dvr' => false, + 'lowLatency' => false + ] ] ]; @@ -1316,8 +1369,7 @@ public function startCaptions( ?int $maxDuration = null, ?bool $partialCaptions = null, ?string $statusCallbackUrl = null - ): array - { + ): array { return $this->client->startCaptions( $sessionId, $token, diff --git a/src/OpenTok/Session.php b/src/OpenTok/Session.php index 2da426d..c9b437c 100644 --- a/src/OpenTok/Session.php +++ b/src/OpenTok/Session.php @@ -154,9 +154,9 @@ public function __toString() * * @return string The token string. */ - public function generateToken($options = array()) + public function generateToken($options = array(), bool $legacy = false) { - return $this->opentok->generateToken($this->sessionId, $options); + return $this->opentok->generateToken($this->sessionId, $options, $legacy); } /** diff --git a/tests/OpenTokTest/OpenTokTest.php b/tests/OpenTokTest/OpenTokTest.php index a014cdb..9b2c78b 100644 --- a/tests/OpenTokTest/OpenTokTest.php +++ b/tests/OpenTokTest/OpenTokTest.php @@ -2,6 +2,8 @@ namespace OpenTokTest; +use Lcobucci\JWT\Configuration; +use Lcobucci\JWT\Token\Plain; use OpenTok\Render; use OpenTok\Role; use OpenTok\Layout; @@ -582,7 +584,7 @@ public function testGeneratesToken(): void $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); // Act - $token = $opentok->generateToken($sessionId); + $token = $opentok->generateToken($sessionId, [], true); // Assert $this->assertIsString($token); @@ -613,7 +615,7 @@ public function testGeneratesTokenWithRole(): void $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); // Act - $token = $opentok->generateToken($sessionId, array('role' => Role::MODERATOR)); + $token = $opentok->generateToken($sessionId, array('role' => Role::MODERATOR), true); // Assert $this->assertIsString($token); @@ -645,7 +647,7 @@ public function testGeneratesTokenWithExpireTime(): void // Act // expires in one hour (60 seconds * 60 minutes) $inOneHour = time() + (60 * 60); - $token = $opentok->generateToken($sessionId, array('expireTime' => $inOneHour )); + $token = $opentok->generateToken($sessionId, array('expireTime' => $inOneHour ), true); // Assert $this->assertIsString($token); @@ -675,7 +677,7 @@ public function testGeneratesTokenWithData(): void // Act $userStatus = '{nick:"johnny",status:"hey there fellas!"}'; - $token = $opentok->generateToken($sessionId, array('data' => $userStatus )); + $token = $opentok->generateToken($sessionId, array('data' => $userStatus), true); // Assert $this->assertIsString($token); @@ -712,7 +714,7 @@ public function testGeneratesTokenWithInitialLayoutClassList(): void // Act $token = $opentok->generateToken($sessionId, array( 'initialLayoutClassList' => $initialLayouClassList - )); + ), true); // Assert $this->assertIsString($token); @@ -737,7 +739,32 @@ public function testFailsWhenGeneratingTokenUsingInvalidRole(): void { $this->expectException('InvalidArgumentException'); $this->setupOT(); - $token = $this->opentok->generateToken('SESSIONID', array('role' => 'notarole')); + $token = $this->opentok->generateToken('SESSIONID', array('role' => 'notarole'), true); + } + + public function testWillCreateJwt(): void + { + $openTok = new OpenTok('my-api-key', 'my-super-long-and-cool-api-secret'); + $token = $openTok->generateToken('some-token-value'); + + $config = Configuration::forSymmetricSigner( + new \Lcobucci\JWT\Signer\Hmac\Sha256(), + \Lcobucci\JWT\Signer\Key\InMemory::plainText('my-super-long-and-cool-api-secret') + ); + + $token = $config->parser()->parse($token); + $this->assertInstanceOf(Plain::class, $token); + + $this->assertTrue($config->validator()->validate($token, new \Lcobucci\JWT\Validation\Constraint\SignedWith( + $config->signer(), + $config->signingKey() + ))); + + $this->assertEquals('my-api-key', $token->claims()->get('iss')); + $this->assertEquals('some-token-value', $token->claims()->get('session_id')); + $this->assertEquals('publisher', $token->claims()->get('role')); + $this->assertEquals('project', $token->claims()->get('ist')); + $this->assertEquals('session.connect', $token->claims()->get('scope')); } public function testStartsArchive(): void @@ -763,7 +790,7 @@ public function testStartsArchive(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -836,7 +863,7 @@ public function testStartsArchiveInMultiTagMode(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -875,7 +902,7 @@ public function testStartsArchiveInManualMode(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -942,7 +969,7 @@ public function testStartsArchiveNamed(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -988,7 +1015,7 @@ public function testStartsArchiveNamedDeprecated(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1030,7 +1057,7 @@ public function testStartsArchiveAudioOnly(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1075,7 +1102,7 @@ public function testStartsArchiveIndividualOutput(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1119,7 +1146,7 @@ public function testStartsArchiveResolutionSD(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1162,7 +1189,7 @@ public function testStartsArchiveResolutionHD(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1201,7 +1228,7 @@ public function testStopsArchive(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive/'.$archiveId.'/stop', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive/' . $archiveId . '/stop', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1237,7 +1264,7 @@ public function testGetsArchive(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive/'.$archiveId, $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive/' . $archiveId, $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1298,7 +1325,7 @@ public function testDeletesArchive(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('DELETE', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive/'.$archiveId, $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive/' . $archiveId, $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1332,7 +1359,7 @@ public function testListsArchives(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1363,7 +1390,7 @@ public function testListsArchivesWithOffsetAndCount(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1398,7 +1425,7 @@ public function testListsArchivesWithSessionId(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1407,9 +1434,9 @@ public function testListsArchivesWithSessionId(): void $this->assertInstanceOf('OpenTok\ArchiveList', $archiveList); $this->assertEquals(2, $archiveList->totalCount()); $this->assertEquals($sessionId, $archiveList->getItems()[0]->sessionId); - $this->assertEquals($sessionId, $archiveList->getItems()[1]->sessionId); + $this->assertEquals($sessionId, $archiveList->getItems()[1]->sessionId); $this->assertEquals('b8f64de1-e218-4091-9544-4cbf369fc238', $archiveList->getItems()[0]->id); - $this->assertEquals('832641bf-5dbf-41a1-ad94-fea213e59a92', $archiveList->getItems()[1]->id); + $this->assertEquals('832641bf-5dbf-41a1-ad94-fea213e59a92', $archiveList->getItems()[1]->id); } public function testFailsWhenListingArchivesWithTooLargeCount(): void @@ -1472,7 +1499,7 @@ public function testForceDisconnect(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('DELETE', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/connection/'.$connectionId, $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/session/' . $sessionId . '/connection/' . $connectionId, $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1497,7 +1524,7 @@ public function testForceDisconnectConnectionException(): void $connectionId = '063e72a4-64b4-43c8-9da5-eca071daab89'; - $this->expectException('OpenTok\Exception\ForceDisconnectConnectionException'); + $this->expectException('OpenTok\Exception\ForceDisconnectConnectionException'); // Act $this->opentok->forceDisconnect($sessionId, $connectionId); @@ -1558,202 +1585,202 @@ public function testCanConnectAudioStreamWithWebsocket() $this->assertEquals('7aebb3a4-3d86-4962-b317-afb73e05439d', $response['connectionId']); } - public function testCanStartBroadcastWithRmtp() - { - $this->setupOTWithMocks([[ - 'code' => 200, - 'headers' => [ - 'Content-Type' => 'application/json' - ], - 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_default' - ]]); - - $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; - - $options = [ - 'outputs' => [ - 'hls' => [ - 'dvr' => true, - 'lowLatency' => false - ], - 'rtmp' => [ - [ - 'id' => 'foo', - 'serverUrl' => 'rtmps://myfooserver/myfooapp', - 'streamName' => 'myfoostream' - ], - [ - 'id' => 'bar', - 'serverUrl' => 'rtmps://myfooserver/mybarapp', - 'streamName' => 'mybarstream' - ], - ] - ] - ]; - - $broadcast = $this->opentok->startBroadcast($sessionId, $options); - $this->assertTrue($broadcast->isHls); - $this->assertFalse($broadcast->isDvr); - $this->assertFalse($broadcast->isLowLatency); - $this->assertTrue(array_key_exists('rtmp', $broadcast->broadcastUrls)); - } - - public function testCannotStartBroadcastWithOver5RtmpChannels(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->setupOTWithMocks([[ - 'code' => 200, - 'headers' => [ - 'Content-Type' => 'application/json' - ], - 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_default' - ]]); - - $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; - - $options = [ - 'outputs' => [ - 'hls' => [ - 'dvr' => true, - 'lowLatency' => false - ], - 'rtmp' => [ - [ - 'id' => 'one', - 'serverUrl' => 'rtmps://myfooserver/one', - 'streamName' => 'one' - ], - [ - 'id' => 'two', - 'serverUrl' => 'rtmps://myfooserver/two', - 'streamName' => 'two' - ], - [ - 'id' => 'three', - 'serverUrl' => 'rtmps://myfooserver/three', - 'streamName' => 'three' - ], - [ - 'id' => 'four', - 'serverUrl' => 'rtmps://myfooserver/four', - 'streamName' => 'four' - ], - [ - 'id' => 'five', - 'serverUrl' => 'rtmps://myfooserver/five', - 'streamName' => 'five' - ], - [ - 'id' => 'six', - 'serverUrl' => 'rtmps://myfooserver/six', - 'streamName' => 'six' - ], - ] - ] - ]; - - $broadcast = $this->opentok->startBroadcast($sessionId, $options); - } - - public function testCanStartBroadcastWithDefaultHlsOptions(): void - { - $this->setupOTWithMocks([[ - 'code' => 200, - 'headers' => [ - 'Content-Type' => 'application/json' - ], - 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_default' - ]]); - - $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; - - $broadcast = $this->opentok->startBroadcast($sessionId); - $this->assertTrue($broadcast->isHls); - $this->assertFalse($broadcast->isDvr); - $this->assertFalse($broadcast->isLowLatency); + public function testCanStartBroadcastWithRmtp() + { + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_default' + ]]); + + $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; + + $options = [ + 'outputs' => [ + 'hls' => [ + 'dvr' => true, + 'lowLatency' => false + ], + 'rtmp' => [ + [ + 'id' => 'foo', + 'serverUrl' => 'rtmps://myfooserver/myfooapp', + 'streamName' => 'myfoostream' + ], + [ + 'id' => 'bar', + 'serverUrl' => 'rtmps://myfooserver/mybarapp', + 'streamName' => 'mybarstream' + ], + ] + ] + ]; + + $broadcast = $this->opentok->startBroadcast($sessionId, $options); + $this->assertTrue($broadcast->isHls); + $this->assertFalse($broadcast->isDvr); + $this->assertFalse($broadcast->isLowLatency); + $this->assertTrue(array_key_exists('rtmp', $broadcast->broadcastUrls)); + } + + public function testCannotStartBroadcastWithOver5RtmpChannels(): void + { + $this->expectException(InvalidArgumentException::class); + + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_default' + ]]); + + $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; + + $options = [ + 'outputs' => [ + 'hls' => [ + 'dvr' => true, + 'lowLatency' => false + ], + 'rtmp' => [ + [ + 'id' => 'one', + 'serverUrl' => 'rtmps://myfooserver/one', + 'streamName' => 'one' + ], + [ + 'id' => 'two', + 'serverUrl' => 'rtmps://myfooserver/two', + 'streamName' => 'two' + ], + [ + 'id' => 'three', + 'serverUrl' => 'rtmps://myfooserver/three', + 'streamName' => 'three' + ], + [ + 'id' => 'four', + 'serverUrl' => 'rtmps://myfooserver/four', + 'streamName' => 'four' + ], + [ + 'id' => 'five', + 'serverUrl' => 'rtmps://myfooserver/five', + 'streamName' => 'five' + ], + [ + 'id' => 'six', + 'serverUrl' => 'rtmps://myfooserver/six', + 'streamName' => 'six' + ], + ] + ] + ]; + + $broadcast = $this->opentok->startBroadcast($sessionId, $options); + } + + public function testCanStartBroadcastWithDefaultHlsOptions(): void + { + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_default' + ]]); + + $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; + + $broadcast = $this->opentok->startBroadcast($sessionId); + $this->assertTrue($broadcast->isHls); + $this->assertFalse($broadcast->isDvr); + $this->assertFalse($broadcast->isLowLatency); $this->assertEquals('live', $broadcast->broadcastUrls['rtmp']['foo']['status']); - } - - public function testCanStartBroadcastWithDvrEnabled(): void - { - $this->setupOTWithMocks([[ - 'code' => 200, - 'headers' => [ - 'Content-Type' => 'application/json' - ], - 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_dvr' - ]]); - - $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; - - $options = [ - 'outputs' => [ - 'hls' => [ - 'dvr' => true, - 'lowLatency' => false - ] - ] - ]; - - $broadcast = $this->opentok->startBroadcast($sessionId, $options); - $this->assertTrue($broadcast->isHls); - $this->assertTrue($broadcast->isDvr); - $this->assertFalse($broadcast->isLowLatency); - } - - public function testCanStartBroadcastWithLowLatencyEnabled(): void - { - $this->setupOTWithMocks([[ - 'code' => 200, - 'headers' => [ - 'Content-Type' => 'application/json' - ], - 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_ll' - ]]); - - $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; - - $options = [ - 'outputs' => [ - 'hls' => [ - 'dvr' => true, - 'lowLatency' => false - ] - ] - ]; - - $broadcast = $this->opentok->startBroadcast($sessionId, $options); - $this->assertTrue($broadcast->isHls); - $this->assertFalse($broadcast->isDvr); - $this->assertTrue($broadcast->isLowLatency); - } - - public function testCannotStartBroadcastWithBothHlsAndDvrEnabled(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->setupOTWithMocks([[ - 'code' => 200, - 'headers' => [ - 'Content-Type' => 'application/json' - ], - 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_ll' - ]]); - - $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; - - $options = [ - 'outputs' => [ - 'hls' => [ - 'dvr' => true, - 'lowLatency' => true - ] - ] - ]; - - $broadcast = $this->opentok->startBroadcast($sessionId, $options); - } + } + + public function testCanStartBroadcastWithDvrEnabled(): void + { + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_dvr' + ]]); + + $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; + + $options = [ + 'outputs' => [ + 'hls' => [ + 'dvr' => true, + 'lowLatency' => false + ] + ] + ]; + + $broadcast = $this->opentok->startBroadcast($sessionId, $options); + $this->assertTrue($broadcast->isHls); + $this->assertTrue($broadcast->isDvr); + $this->assertFalse($broadcast->isLowLatency); + } + + public function testCanStartBroadcastWithLowLatencyEnabled(): void + { + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_ll' + ]]); + + $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; + + $options = [ + 'outputs' => [ + 'hls' => [ + 'dvr' => true, + 'lowLatency' => false + ] + ] + ]; + + $broadcast = $this->opentok->startBroadcast($sessionId, $options); + $this->assertTrue($broadcast->isHls); + $this->assertFalse($broadcast->isDvr); + $this->assertTrue($broadcast->isLowLatency); + } + + public function testCannotStartBroadcastWithBothHlsAndDvrEnabled(): void + { + $this->expectException(InvalidArgumentException::class); + + $this->setupOTWithMocks([[ + 'code' => 200, + 'headers' => [ + 'Content-Type' => 'application/json' + ], + 'path' => '/v2/project/APIKEY/broadcast/BROADCASTID/start_ll' + ]]); + + $sessionId = '2_MX44NTQ1MTF-fjE0NzI0MzU2MDUyMjN-eVgwNFJhZmR6MjdockFHanpxNzBXaEFXfn4'; + + $options = [ + 'outputs' => [ + 'hls' => [ + 'dvr' => true, + 'lowLatency' => true + ] + ] + ]; + + $broadcast = $this->opentok->startBroadcast($sessionId, $options); + } public function testStartsBroadcast(): void { @@ -1773,7 +1800,7 @@ public function testStartsBroadcast(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1838,7 +1865,7 @@ public function testStartsBroadcastWithMultiBroadcastTag(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1903,7 +1930,7 @@ public function testStartsBroadcastInManualStreamMode(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1955,7 +1982,7 @@ public function testStartBroadcastWithOptions(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -1969,7 +1996,7 @@ public function testStartBroadcastWithOptions(): void $this->assertIsString($broadcast->id); $this->assertEquals($sessionId, $broadcast->sessionId); $this->assertEquals($maxDuration, $broadcast->maxDuration); - $this->assertEquals($resolution, $broadcast->resolution); + $this->assertEquals($resolution, $broadcast->resolution); $this->assertIsArray($broadcast->broadcastUrls); $this->assertArrayHasKey('hls', $broadcast->broadcastUrls); $this->assertIsString($broadcast->broadcastUrls['hls']); @@ -2000,7 +2027,7 @@ public function testStopsBroadcast(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast/'.$broadcastId.'/stop', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast/' . $broadcastId . '/stop', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2032,7 +2059,7 @@ public function testGetsBroadcast(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast/'.$broadcastId, $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast/' . $broadcastId, $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2150,7 +2177,7 @@ public function testUpdatesBroadcastLayoutWithPredefined(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('PUT', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast/'.$broadcastId.'/layout', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast/' . $broadcastId . '/layout', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2190,7 +2217,7 @@ public function testUpdatesBroadcastLayoutWithCustom(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('PUT', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/broadcast/'.$broadcastId.'/layout', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/broadcast/' . $broadcastId . '/layout', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2231,7 +2258,7 @@ public function testUpdatesStreamLayoutClassList(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('PUT', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/stream/'.$streamId, $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/session/' . $sessionId . '/stream/' . $streamId, $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2263,11 +2290,11 @@ public function testGetStream(): void // Act $streamData = $this->opentok->getStream($sessionId, $streamId); // Assert - $this->assertCount(1, $this->historyContainer); + $this->assertCount(1, $this->historyContainer); $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/stream/'.$streamId, $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/session/' . $sessionId . '/stream/' . $streamId, $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2608,7 +2635,7 @@ public function testSignalData(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/signal', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/session/' . $sessionId . '/signal', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2616,7 +2643,7 @@ public function testSignalData(): void $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); $body = json_decode($request->getBody()); $this->assertEquals('apple', $body->data); - $this->assertEquals('signal type sample', $body->type); + $this->assertEquals('signal type sample', $body->type); } public function testSignalWithConnectionId(): void @@ -2644,7 +2671,7 @@ public function testSignalWithConnectionId(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('POST', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/connection/'.$connectionId.'/signal', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/session/' . $sessionId . '/connection/' . $connectionId . '/signal', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2652,7 +2679,7 @@ public function testSignalWithConnectionId(): void $this->assertEquals(true, TestHelpers::validateOpenTokAuthHeader($this->API_KEY, $this->API_SECRET, $authString)); $body = json_decode($request->getBody()); $this->assertEquals('random message', $body->data); - $this->assertEquals('rest', $body->type); + $this->assertEquals('rest', $body->type); } /** @@ -2725,7 +2752,6 @@ public function testSignalUnexpectedValueException(): void // Act $this->opentok->signal($sessionId, $payload, $connectionId); - } public function testListStreams(): void @@ -2753,7 +2779,7 @@ public function testListStreams(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('GET', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/session/'.$sessionId.'/stream/', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/session/' . $sessionId . '/stream/', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2784,7 +2810,7 @@ public function testsSetArchiveLayoutWithPredefined(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('PUT', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive/'.$archiveId.'/layout', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive/' . $archiveId . '/layout', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2824,7 +2850,7 @@ public function testsSetArchiveLayoutWithCustom(): void $request = $this->historyContainer[0]['request']; $this->assertEquals('PUT', strtoupper($request->getMethod())); - $this->assertEquals('/v2/project/'.$this->API_KEY.'/archive/'.$archiveId.'/layout', $request->getUri()->getPath()); + $this->assertEquals('/v2/project/' . $this->API_KEY . '/archive/' . $archiveId . '/layout', $request->getUri()->getPath()); $this->assertEquals('api.opentok.com', $request->getUri()->getHost()); $this->assertEquals('https', $request->getUri()->getScheme()); @@ -2931,4 +2957,3 @@ public function testCanStopCaptions(): void $this->assertTrue($result); } } - diff --git a/tests/OpenTokTest/SessionTest.php b/tests/OpenTokTest/SessionTest.php index 4c59d38..ac12a69 100644 --- a/tests/OpenTokTest/SessionTest.php +++ b/tests/OpenTokTest/SessionTest.php @@ -156,7 +156,7 @@ public function testGeneratesToken() $opentok = new OpenTok($bogusApiKey, $bogusApiSecret); $session = new Session($opentok, $sessionId); - $token = $session->generateToken(); + $token = $session->generateToken([], true); $this->assertIsString($token); $decodedToken = TestHelpers::decodeToken($token);