2019-08-31 22:43:58 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Connection module handling all connections to a datacenter.
|
|
|
|
*
|
|
|
|
* This file is part of MadelineProto.
|
|
|
|
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
* See the GNU Affero General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License along with MadelineProto.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* @author Daniil Gentili <daniil@daniil.it>
|
|
|
|
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
|
|
|
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
|
|
|
*
|
|
|
|
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace danog\MadelineProto;
|
|
|
|
|
2019-09-01 14:07:04 +02:00
|
|
|
use danog\MadelineProto\AuthKey\AuthKey;
|
2019-09-01 23:39:29 +02:00
|
|
|
use danog\MadelineProto\AuthKey\PermAuthKey;
|
|
|
|
use danog\MadelineProto\AuthKey\TempAuthKey;
|
2019-08-31 22:43:58 +02:00
|
|
|
use danog\MadelineProto\Stream\ConnectionContext;
|
2019-09-02 14:37:30 +02:00
|
|
|
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
|
|
|
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
2019-09-01 14:07:04 +02:00
|
|
|
use JsonSerializable;
|
2019-08-31 22:43:58 +02:00
|
|
|
|
2019-09-01 14:07:04 +02:00
|
|
|
class DataCenterConnection implements JsonSerializable
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Temporary auth key.
|
|
|
|
*
|
2019-09-01 23:39:29 +02:00
|
|
|
* @var TempAuthKey|null
|
2019-08-31 22:43:58 +02:00
|
|
|
*/
|
|
|
|
private $tempAuthKey;
|
|
|
|
/**
|
|
|
|
* Permanent auth key.
|
|
|
|
*
|
2019-09-01 23:39:29 +02:00
|
|
|
* @var PermAuthKey|null
|
2019-08-31 22:43:58 +02:00
|
|
|
*/
|
|
|
|
private $authKey;
|
|
|
|
|
|
|
|
/**
|
2019-09-01 01:52:28 +02:00
|
|
|
* Connections open to a certain DC.
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
2019-09-01 14:07:04 +02:00
|
|
|
* @var array<string, Connection>
|
2019-08-31 22:43:58 +02:00
|
|
|
*/
|
|
|
|
private $connections = [];
|
2019-09-01 01:52:28 +02:00
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Connection weights.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
2019-09-01 14:07:04 +02:00
|
|
|
* @var array<string, int>
|
2019-09-01 01:52:28 +02:00
|
|
|
*/
|
|
|
|
private $availableConnections = [];
|
2019-08-31 22:43:58 +02:00
|
|
|
|
|
|
|
/**
|
2019-09-01 01:52:28 +02:00
|
|
|
* Main API instance.
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
|
|
|
* @var \danog\MadelineProto\MTProto
|
|
|
|
*/
|
|
|
|
private $API;
|
|
|
|
|
|
|
|
/**
|
2019-09-01 01:52:28 +02:00
|
|
|
* Connection context.
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
|
|
|
* @var ConnectionContext
|
|
|
|
*/
|
|
|
|
private $ctx;
|
|
|
|
|
|
|
|
/**
|
2019-09-01 01:52:28 +02:00
|
|
|
* DC ID.
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $datacenter;
|
2019-09-01 01:52:28 +02:00
|
|
|
|
|
|
|
/**
|
2019-09-01 23:39:29 +02:00
|
|
|
* Linked DC ID.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
2019-09-01 23:39:29 +02:00
|
|
|
* @var string
|
2019-09-01 01:52:28 +02:00
|
|
|
*/
|
2019-09-01 23:39:29 +02:00
|
|
|
private $linked;
|
2019-09-01 01:52:28 +02:00
|
|
|
|
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Loop to keep weights at sane value.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
|
|
|
* @var \danog\MadelineProto\Loop\Generic\PeriodicLoop
|
|
|
|
*/
|
|
|
|
private $robinLoop;
|
|
|
|
|
2019-09-02 14:37:30 +02:00
|
|
|
/**
|
|
|
|
* Decrement roundrobin weight by this value if busy reading.
|
|
|
|
*
|
|
|
|
* @var integer
|
|
|
|
*/
|
|
|
|
private $decRead = 1;
|
|
|
|
/**
|
|
|
|
* Decrement roundrobin weight by this value if busy writing.
|
|
|
|
*
|
|
|
|
* @var integer
|
|
|
|
*/
|
|
|
|
private $decWrite = 10;
|
|
|
|
|
2019-08-31 22:43:58 +02:00
|
|
|
/**
|
|
|
|
* Get auth key.
|
|
|
|
*
|
|
|
|
* @param boolean $temp Whether to fetch the temporary auth key
|
|
|
|
*
|
2019-09-01 14:07:04 +02:00
|
|
|
* @return AuthKey
|
2019-08-31 22:43:58 +02:00
|
|
|
*/
|
2019-09-01 14:07:04 +02:00
|
|
|
public function getAuthKey(bool $temp = true): AuthKey
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
|
|
|
return $this->{$temp ? 'tempAuthKey' : 'authKey'};
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Check if auth key is present.
|
|
|
|
*
|
2019-09-01 23:39:29 +02:00
|
|
|
* @param boolean|null $temp Whether to fetch the temporary auth key
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function hasAuthKey(bool $temp = true): bool
|
|
|
|
{
|
2019-09-01 23:39:29 +02:00
|
|
|
return $this->{$temp ? 'tempAuthKey' : 'authKey'} !== null && $this->{$temp ? 'tempAuthKey' : 'authKey'}->hasAuthKey();
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Set auth key.
|
|
|
|
*
|
2019-09-01 14:07:04 +02:00
|
|
|
* @param AuthKey|null $key The auth key
|
2019-09-01 23:39:29 +02:00
|
|
|
* @param boolean|null $temp Whether to set the temporary auth key
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2019-09-01 14:07:04 +02:00
|
|
|
public function setAuthKey(?AuthKey $key, bool $temp = true)
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
|
|
|
$this->{$temp ? 'tempAuthKey' : 'authKey'} = $key;
|
|
|
|
}
|
|
|
|
|
2019-09-01 23:39:29 +02:00
|
|
|
/**
|
|
|
|
* Get temporary authorization key.
|
|
|
|
*
|
|
|
|
* @return AuthKey
|
|
|
|
*/
|
|
|
|
public function getTempAuthKey(): TempAuthKey
|
|
|
|
{
|
|
|
|
return $this->getAuthKey(true);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Get permanent authorization key.
|
|
|
|
*
|
|
|
|
* @return AuthKey
|
|
|
|
*/
|
|
|
|
public function getPermAuthKey(): PermAuthKey
|
|
|
|
{
|
|
|
|
return $this->getAuthKey(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if has temporary authorization key.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function hasTempAuthKey(): bool
|
|
|
|
{
|
|
|
|
return $this->hasAuthKey(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if has permanent authorization key.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function hasPermAuthKey(): bool
|
|
|
|
{
|
|
|
|
return $this->hasAuthKey(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set temporary authorization key.
|
|
|
|
*
|
|
|
|
* @param TempAuthKey|null $key Auth key
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setTempAuthKey(?TempAuthKey $key)
|
|
|
|
{
|
|
|
|
return $this->setAuthKey($key, true);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Set permanent authorization key.
|
|
|
|
*
|
|
|
|
* @param PermAuthKey|null $key Auth key
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setPermAuthKey(?PermAuthKey $key)
|
|
|
|
{
|
|
|
|
return $this->setAuthKey($key, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bind temporary and permanent auth keys.
|
|
|
|
*
|
|
|
|
* @param bool $pfs Whether to bind using PFS
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function bind(bool $pfs = true)
|
|
|
|
{
|
|
|
|
$this->tempAuthKey->bind($this->authKey, $pfs);
|
|
|
|
}
|
2019-08-31 22:43:58 +02:00
|
|
|
/**
|
|
|
|
* Check if we are logged in.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function isAuthorized(): bool
|
|
|
|
{
|
2019-09-01 23:39:29 +02:00
|
|
|
return $this->hasTempAuthKey() ? $this->getTempAuthKey()->isAuthorized() : false;
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the authorized boolean.
|
|
|
|
*
|
|
|
|
* @param boolean $authorized Whether we are authorized
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function authorized(bool $authorized)
|
|
|
|
{
|
2019-09-01 23:39:29 +02:00
|
|
|
$this->getTempAuthKey()->authorized($authorized);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Link permanent authorization info of main DC to media DC.
|
|
|
|
*
|
|
|
|
* @param string $dc Main DC ID
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function link(string $dc)
|
|
|
|
{
|
|
|
|
$this->linked = $dc;
|
|
|
|
$this->authKey = &$this->API->datacenter->getDataCenterConnection($dc)->authKey;
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 14:07:04 +02:00
|
|
|
/**
|
|
|
|
* Reset MTProto sessions.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function resetSession()
|
|
|
|
{
|
|
|
|
foreach ($this->connections as $socket) {
|
|
|
|
$socket->resetSession();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Flush all pending packets.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function flush()
|
|
|
|
{
|
|
|
|
foreach ($this->connections as $socket) {
|
|
|
|
$socket->flush();
|
|
|
|
}
|
|
|
|
}
|
2019-09-01 23:39:29 +02:00
|
|
|
|
2019-08-31 22:43:58 +02:00
|
|
|
/**
|
2019-09-01 01:52:28 +02:00
|
|
|
* Get connection context.
|
2019-08-31 22:43:58 +02:00
|
|
|
*
|
|
|
|
* @return ConnectionContext
|
|
|
|
*/
|
|
|
|
public function getCtx(): ConnectionContext
|
|
|
|
{
|
|
|
|
return $this->ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connect function.
|
|
|
|
*
|
|
|
|
* @param ConnectionContext $ctx Connection context
|
|
|
|
*
|
|
|
|
* @return \Generator
|
|
|
|
*/
|
|
|
|
public function connect(ConnectionContext $ctx): \Generator
|
|
|
|
{
|
2019-09-01 01:52:28 +02:00
|
|
|
$this->API->logger->logger("Trying shared connection via $ctx", \danog\MadelineProto\Logger::WARNING);
|
2019-08-31 22:43:58 +02:00
|
|
|
|
|
|
|
$this->ctx = $ctx->getCtx();
|
|
|
|
$this->datacenter = $ctx->getDc();
|
|
|
|
$media = $ctx->isMedia();
|
|
|
|
|
|
|
|
$count = $media ? $this->API->settings['connection_settings']['media_socket_count'] : 1;
|
|
|
|
|
2019-09-01 01:52:28 +02:00
|
|
|
if ($count > 1) {
|
|
|
|
if (!$this->robinLoop) {
|
|
|
|
$this->robinLoop = new PeriodicLoop($this, [$this, 'even'], "Robin loop DC {$this->datacenter}", 10);
|
|
|
|
}
|
|
|
|
$this->robinLoop->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
$incRead = $media ? 5 : 1;
|
|
|
|
|
2019-08-31 22:43:58 +02:00
|
|
|
$this->connections = [];
|
2019-09-01 01:52:28 +02:00
|
|
|
$this->availableConnections = [];
|
2019-08-31 22:43:58 +02:00
|
|
|
for ($x = 0; $x < $count; $x++) {
|
2019-09-01 01:52:28 +02:00
|
|
|
$this->availableConnections[$x] = 0;
|
2019-08-31 23:07:20 +02:00
|
|
|
$this->connections[$x] = new Connection();
|
2019-09-02 14:37:30 +02:00
|
|
|
$this->connections[$x]->setExtra($this, $x);
|
2019-09-01 14:07:04 +02:00
|
|
|
yield $this->connections[$x]->connect($ctx);
|
2019-08-31 22:43:58 +02:00
|
|
|
$ctx = $this->ctx->getCtx();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 01:52:28 +02:00
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Close all connections to DC.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function disconnect()
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
2019-09-01 01:52:28 +02:00
|
|
|
$this->API->logger->logger("Disconnecting from shared DC {$this->datacenter}");
|
|
|
|
if ($this->robinLoop) {
|
|
|
|
$this->robinLoop->signal(true);
|
|
|
|
$this->robinLoop = null;
|
|
|
|
}
|
|
|
|
foreach ($this->connections as $connection) {
|
|
|
|
$connection->disconnect();
|
|
|
|
}
|
|
|
|
$this->connections = [];
|
|
|
|
$this->availableConnections = [];
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 01:52:28 +02:00
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Reconnect to DC.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
|
|
|
* @return \Generator
|
|
|
|
*/
|
|
|
|
public function reconnect(): \Generator
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
2019-09-01 01:52:28 +02:00
|
|
|
$this->API->logger->logger("Reconnecting shared DC {$this->datacenter}");
|
|
|
|
$this->disconnect();
|
|
|
|
yield $this->connect($this->ctx);
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 23:39:29 +02:00
|
|
|
/**
|
|
|
|
* Get connection for authorization.
|
|
|
|
*
|
|
|
|
* @return Connection
|
|
|
|
*/
|
|
|
|
public function getAuthConnection(): Connection
|
|
|
|
{
|
|
|
|
return $this->connections[0];
|
|
|
|
}
|
|
|
|
|
2019-09-01 01:52:28 +02:00
|
|
|
/**
|
|
|
|
* Get best socket in round robin.
|
|
|
|
*
|
|
|
|
* @return Connection
|
|
|
|
*/
|
|
|
|
public function getConnection(): Connection
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
2019-09-01 14:07:04 +02:00
|
|
|
if (\count($this->availableConnections) === 1) {
|
2019-09-01 01:52:28 +02:00
|
|
|
return $this->connections[0];
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
2019-09-01 14:07:04 +02:00
|
|
|
\max($this->availableConnections);
|
|
|
|
$key = \key($this->availableConnections);
|
2019-09-01 01:52:28 +02:00
|
|
|
// Decrease to implement round robin
|
|
|
|
$this->availableConnections[$key]--;
|
|
|
|
return $this->connections[$key];
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
|
2019-09-01 01:52:28 +02:00
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Even out round robin values.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function even()
|
2019-08-31 22:43:58 +02:00
|
|
|
{
|
2019-09-01 01:52:28 +02:00
|
|
|
if (\min($this->availableConnections) < 1000) {
|
|
|
|
foreach ($this->availableConnections as &$value) {
|
|
|
|
$value += 1000;
|
|
|
|
}
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-02 14:37:30 +02:00
|
|
|
/**
|
|
|
|
* Indicate that one of the sockets is busy reading.
|
|
|
|
*
|
|
|
|
* @param boolean $reading Whether we're busy reading
|
|
|
|
* @param int $x Connection ID
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function reading(bool $reading, int $x)
|
|
|
|
{
|
|
|
|
$this->availableConnections[$x] += $reading ? -$this->decRead : $this->decRead;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Indicate that one of the sockets is busy writing.
|
|
|
|
*
|
|
|
|
* @param boolean $writing Whether we're busy writing
|
|
|
|
* @param int $x Connection ID
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function writing(bool $writing, int $x)
|
|
|
|
{
|
|
|
|
$this->availableConnections[$x] += $writing ? -$this->decWrite : $this->decWrite;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-01 01:52:28 +02:00
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Set main instance.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
|
|
|
* @param MTProto $API Main instance
|
2019-09-01 14:07:04 +02:00
|
|
|
*
|
2019-09-01 01:52:28 +02:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setExtra(MTProto $API)
|
|
|
|
{
|
|
|
|
$this->API = $API;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-09-01 14:07:04 +02:00
|
|
|
* Get main instance.
|
2019-09-01 01:52:28 +02:00
|
|
|
*
|
|
|
|
* @return MTProto
|
|
|
|
*/
|
|
|
|
public function getExtra(): MTProto
|
|
|
|
{
|
|
|
|
return $this->API;
|
|
|
|
}
|
2019-09-02 14:37:30 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if is an HTTP connection.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function isHttp()
|
|
|
|
{
|
|
|
|
return \in_array($this->ctx->getStreamName(), [HttpStream::getName(), HttpsStream::getName()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get DC-specific settings
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getSettings(): array
|
|
|
|
{
|
|
|
|
$dc_config_number = isset($this->API->settings['connection_settings'][$this->datacenter]) ? $this->datacenter : 'all';
|
|
|
|
return $this->API->settings['connection_settings'][$dc_config_number];
|
|
|
|
}
|
|
|
|
|
2019-09-01 14:07:04 +02:00
|
|
|
/**
|
|
|
|
* JSON serialize function.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function jsonSerialize(): array
|
|
|
|
{
|
2019-09-01 23:39:29 +02:00
|
|
|
return $this->linked ?
|
|
|
|
[
|
|
|
|
'linked' => $this->linked,
|
|
|
|
'tempAuthKey' => $this->tempAuthKey
|
|
|
|
] :
|
|
|
|
[
|
2019-09-01 14:07:04 +02:00
|
|
|
'authKey' => $this->authKey,
|
2019-09-01 23:39:29 +02:00
|
|
|
'tempAuthKey' => $this->tempAuthKey
|
2019-09-01 14:07:04 +02:00
|
|
|
];
|
|
|
|
}
|
2019-08-31 22:43:58 +02:00
|
|
|
/**
|
|
|
|
* Sleep function.
|
|
|
|
*
|
|
|
|
* @internal
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function __sleep()
|
|
|
|
{
|
2019-09-01 23:39:29 +02:00
|
|
|
return $this->linked ? ['linked', 'tempAuthKey'] : ['linked', 'authKey', 'tempAuthKey'];
|
2019-08-31 22:43:58 +02:00
|
|
|
}
|
|
|
|
}
|