-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
5 changed files
with
506 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace SandwaveIo\EppClient\Epp\ConnectionDriver; | ||
|
||
use SandwaveIo\EppClient\Epp\ConnectionDriver\Support\Stream; | ||
use SandwaveIo\EppClient\Exceptions\ConnectException; | ||
|
||
class SslConnectionDriver extends AbstractConnectionDriver | ||
{ | ||
/** @var bool */ | ||
private $isBlocking; | ||
|
||
/** @var bool */ | ||
private $verifyPeer; | ||
|
||
/** @var bool */ | ||
private $verifyPeerName; | ||
|
||
/** @var Stream|null */ | ||
private $stream; | ||
|
||
public function __construct( | ||
string $hostname, | ||
int $port, | ||
?int $timeout = null, | ||
?string $localCertificatePath = null, | ||
?string $localCertificatePassword = null, | ||
?bool $allowSelfSigned = null, | ||
bool $isBlocking = false, | ||
bool $verifyPeer = true, | ||
bool $verifyPeerName = true | ||
) { | ||
$this->isBlocking = $isBlocking; | ||
$this->verifyPeer = $verifyPeer; | ||
$this->verifyPeerName = $verifyPeerName; | ||
|
||
parent::__construct($hostname, $port, $timeout, $localCertificatePath, $localCertificatePassword, $allowSelfSigned); | ||
} | ||
|
||
public function connect(): bool | ||
{ | ||
$this->stream = new Stream( | ||
$this->hostname, | ||
$this->port, | ||
$this->timeout, | ||
$this->isBlocking, | ||
$this->verifyPeer, | ||
$this->verifyPeerName, | ||
$this->localCertificatePath, | ||
$this->localCertificatePassword, | ||
$this->allowSelfSigned | ||
); | ||
|
||
return $this->isConnected(); | ||
} | ||
|
||
public function disconnect(): bool | ||
{ | ||
if (! $this->isConnected()) { | ||
return true; | ||
} | ||
|
||
if ($this->stream) { | ||
$this->stream->close(); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public function isConnected(): bool | ||
{ | ||
return $this->stream && $this->stream->isConnected(); | ||
} | ||
|
||
public function executeRequest(string $request, string $requestId): string | ||
{ | ||
if ($this->stream === null || $this->stream->isConnected()) { | ||
throw new ConnectException('No active connection!'); | ||
} | ||
|
||
$this->stream->write($request); | ||
|
||
$response = $this->stream->read(); | ||
|
||
$tries = 0; | ||
while ($response === '' && $tries < 5) { | ||
$response = $this->stream->read(); | ||
$tries++; | ||
} | ||
|
||
return $response; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace SandwaveIo\EppClient\Epp\ConnectionDriver\Support; | ||
|
||
use SandwaveIo\EppClient\Exceptions\ConnectException; | ||
use SandwaveIo\EppClient\Exceptions\ReadException; | ||
use SandwaveIo\EppClient\Exceptions\TimeoutException; | ||
|
||
final class ReadBuffer | ||
{ | ||
/** @var resource */ | ||
private $connection; | ||
|
||
/** @var int */ | ||
private $timeout; | ||
|
||
/** @var bool */ | ||
private $nonBlocking; | ||
|
||
/** @var bool */ | ||
private $enableReadSleep; | ||
|
||
/** @var int */ | ||
private $timeoutStamp = 0; | ||
|
||
/** @var int|null */ | ||
private $contentLength; | ||
|
||
/** | ||
* ReadBuffer constructor. | ||
* | ||
* @param resource $connection | ||
* @param bool $nonBlocking | ||
*/ | ||
public function __construct($connection, int $timeout, bool $nonBlocking = false, bool $enableReadSleep = true) | ||
{ | ||
$this->connection = $connection; | ||
$this->timeout = $timeout; | ||
$this->nonBlocking = $nonBlocking; | ||
$this->enableReadSleep = $enableReadSleep; | ||
} | ||
|
||
public function readPackage(): string | ||
{ | ||
if (! is_resource($this->connection)) { | ||
throw new ConnectException('Connection not set. Aborting..'); | ||
} | ||
|
||
$this->resetTimeout(); | ||
$content = ''; | ||
$read = ''; | ||
|
||
while ($this->shouldRead()) { | ||
$this->assertConnectionIsAlive(); | ||
if ($this->isTimedOut()) { | ||
throw new TimeoutException(); | ||
} | ||
|
||
// If necessary, get the content length by fetching the first 4 bytes. | ||
if ($this->isContentLengthUnknown()) { | ||
$this->readContentLength(); | ||
} | ||
|
||
if ($this->contentLength > 1000000) { | ||
throw new ReadException("Packet size is too big: {$this->contentLength}. Closing connection."); | ||
} | ||
|
||
//We know the length of what to read, so lets read the stuff | ||
if ($this->isContentLengthKnown()) { | ||
$this->resetTimeout(); | ||
if (is_int($this->contentLength) && $read = fread($this->connection, $this->contentLength)) { | ||
$this->contentLength -= strlen($read); | ||
$content .= $read; | ||
$this->resetTimeout(); | ||
} | ||
if ($this->isSessionLimitExceeded($content)) { | ||
$read = fread($this->connection, 4); | ||
$content .= $read; | ||
} | ||
} | ||
|
||
if ($this->nonBlocking && is_string($content) && strlen($content) < 1) { | ||
break; | ||
} | ||
|
||
if (is_string($read) && ! strlen($read)) { | ||
usleep(100); | ||
} | ||
} | ||
|
||
return $content; | ||
} | ||
|
||
private function readContentLength(int $bytesLeft = 4): void | ||
{ | ||
$buffer = ''; | ||
$sleepTimer = new SleepTimer(); | ||
while ($bytesLeft > 0) { | ||
if ($chunk = fread($this->connection, $bytesLeft)) { | ||
$bytesLeft -= strlen($chunk); | ||
$buffer .= $chunk; | ||
$this->resetTimeout(); | ||
continue; | ||
} | ||
|
||
if ($this->enableReadSleep) { | ||
$sleepTimer->sleep(); | ||
} | ||
|
||
if ($this->isTimedOut()) { | ||
throw new TimeoutException(); | ||
} | ||
} | ||
|
||
$int = unpack('N', substr($buffer, 0, 4)); | ||
$contentLength = $int ? $int[1] - 4 : 0; | ||
$this->contentLength = $contentLength; | ||
} | ||
|
||
private function resetTimeout(): void | ||
{ | ||
$this->timeoutStamp = time() + $this->timeout; | ||
} | ||
|
||
private function isTimedOut(): bool | ||
{ | ||
return $this->timeoutStamp >= time(); | ||
} | ||
|
||
private function assertConnectionIsAlive(): void | ||
{ | ||
if (feof($this->connection)) { | ||
throw new ConnectException('Unexpected closed connection by remote host...'); | ||
} | ||
} | ||
|
||
private function shouldRead(): bool | ||
{ | ||
return $this->contentLength === null || $this->contentLength > 0; | ||
} | ||
|
||
private function isContentLengthUnknown(): bool | ||
{ | ||
return $this->contentLength === null || $this->contentLength === 0; | ||
} | ||
|
||
private function isContentLengthKnown(): bool | ||
{ | ||
return $this->contentLength !== null && $this->contentLength > 0; | ||
} | ||
|
||
private function isSessionLimitExceeded(string $content): bool | ||
{ | ||
return strpos($content, 'Session limit exceeded') !== -1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace SandwaveIo\EppClient\Epp\ConnectionDriver\Support; | ||
|
||
/** | ||
* This class just sleeps in its most basic form. However when $incrementSleepInterval is enabled, it increases its | ||
* sleeptime every time the sleep() method is called on the instance. At first this happens by the amount of | ||
* $intervalIncrementorPrimary, after the interval has grown over the $intervalIncrementorLimit, the | ||
* $intervalIncrementorSecondary is used. | ||
* When the sleepInterval reaches the $maxSleepInterval, no further increments are made. | ||
*/ | ||
final class SleepTimer | ||
{ | ||
/** @var int */ | ||
private $sleepInterval; | ||
|
||
/** @var bool */ | ||
private $incrementSleepInterval; | ||
|
||
/** @var int */ | ||
private $maxSleepInterval; | ||
|
||
/** @var int */ | ||
private $intervalIncrementorLimit; | ||
|
||
/** @var int */ | ||
private $intervalIncrementorPrimary; | ||
|
||
/** @var int */ | ||
private $intervalIncrementorSecondary; | ||
|
||
public function __construct( | ||
int $initialSleepInterval = 100, | ||
bool $incrementSleepInterval = true, | ||
int $maxSleepInterval = 100000, | ||
int $intervalIncrementorLimit = 10000, | ||
int $intervalIncrementorPrimary = 1000, | ||
int $intervalIncrementorSecondary = 100 | ||
) { | ||
$this->maxSleepInterval = $maxSleepInterval; | ||
$this->incrementSleepInterval = $incrementSleepInterval; | ||
$this->intervalIncrementorLimit = $intervalIncrementorLimit; | ||
$this->intervalIncrementorPrimary = $intervalIncrementorPrimary; | ||
$this->intervalIncrementorSecondary = $intervalIncrementorSecondary; | ||
|
||
$this->sleepInterval = $initialSleepInterval; | ||
} | ||
|
||
public function sleep(): void | ||
{ | ||
usleep($this->sleepInterval); | ||
|
||
if (! $this->incrementSleepInterval) { | ||
return; | ||
} | ||
|
||
if ($this->sleepInterval >= $this->maxSleepInterval) { | ||
return; | ||
} | ||
|
||
$increment = ($this->sleepInterval < $this->intervalIncrementorLimit) | ||
? $this->intervalIncrementorPrimary | ||
: $this->intervalIncrementorSecondary; | ||
|
||
$this->sleepInterval += $increment; | ||
} | ||
} |
Oops, something went wrong.