Skip to content

Commit

Permalink
Merge branch '2.x' into 3.x
Browse files Browse the repository at this point in the history
  • Loading branch information
frankdejonge committed Apr 24, 2022
2 parents d785c1d + e902df6 commit b4b33ab
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 4 deletions.
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,15 @@ services:
- "2122:21"
- "30000-30009:30000-30009"
command: "/run.sh -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -j -P localhost"
toxiproxy:
container_name: toxiproxy
restart: unless-stopped
image: ghcr.io/shopify/toxiproxy
command: "-host 0.0.0.0 -config /opt/toxiproxy/config.json"
volumes:
- ./test_files/toxiproxy/toxiproxy.json:/opt/toxiproxy/config.json:ro
ports:
- "8474:8474" # HTTP API
- "8222:8222" # SFTP
- "8121:8121" # FTP
- "8122:8122" # FTPD
75 changes: 75 additions & 0 deletions src/AdapterTestUtilities/ToxiproxyManagement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace League\Flysystem\AdapterTestUtilities;

use GuzzleHttp\Client;

/**
* This class provides a client for the HTTP API provided by the proxy that simulates network issues.
*
* @see https://github.com/shopify/toxiproxy#http-api
*
* @phpstan-type RegisteredProxies 'ftp'|'sftp'|'ftpd'
* @phpstan-type StreamDirection 'upstream'|'downstream'
* @phpstan-type Type 'latency'|'bandwidth'|'slow_close'|'timeout'|'reset_peer'|'slicer'|'limit_data'
* @phpstan-type Attributes array{latency?: int, jitter?: int, rate?: int, delay?: int}
* @phpstan-type Toxic array{name?: string, type: Type, stream?: StreamDirection, toxicity?: float, attributes: Attributes}
*/
final class ToxiproxyManagement
{
/** @var Client */
private $apiClient;

public function __construct(Client $apiClient)
{
$this->apiClient = $apiClient;
}

public static function forServer(string $apiUri = 'http://localhost:8474'): self
{
return new self(
new Client(
[
'base_uri' => $apiUri,
'base_url' => $apiUri, // Compatibility with older versions of Guzzle
]
)
);
}

public function removeAllToxics(): void
{
$this->apiClient->post('/reset');
}

/**
* Simulates a peer reset on the client->server direction.
*
* @param RegisteredProxies $proxyName
*/
public function resetPeerOnRequest(
string $proxyName,
int $timeoutInMilliseconds
): void {
$configuration = [
'type' => 'reset_peer',
'stream' => 'upstream',
'attributes' => ['timeout' => $timeoutInMilliseconds],
];

$this->addToxic($proxyName, $configuration);
}

/**
* Registers a network toxic for the given proxy.
*
* @param RegisteredProxies $proxyName
* @param Toxic $configuration
*/
private function addToxic(string $proxyName, array $configuration): void
{
$this->apiClient->post('/proxies/' . $proxyName . '/toxics', ['json' => $configuration]);
}
}
9 changes: 5 additions & 4 deletions src/PhpseclibV3/SftpConnectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace League\Flysystem\PhpseclibV3;

use League\Flysystem\FilesystemException;
use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Exception\NoKeyLoadedException;
use phpseclib3\Net\SFTP;
use phpseclib3\System\SSH\Agent;
use RuntimeException;
use Throwable;

use function base64_decode;
Expand Down Expand Up @@ -146,7 +146,10 @@ private function setupConnection(): SFTP
$this->authenticate($connection);
} catch (Throwable $exception) {
$connection->disconnect();
throw $exception;

if ($exception instanceof FilesystemException) {
throw $exception;
}
}

return $connection;
Expand Down Expand Up @@ -238,8 +241,6 @@ private function loadPrivateKey(): AsymmetricKey
} catch (NoKeyLoadedException $exception) {
throw new UnableToLoadPrivateKey();
}

throw new RuntimeException();
}

private function authenticateWithAgent(SFTP $connection): void
Expand Down
47 changes: 47 additions & 0 deletions src/PhpseclibV3/SftpConnectionProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace League\Flysystem\PhpseclibV3;

use League\Flysystem\AdapterTestUtilities\ToxiproxyManagement;
use phpseclib3\Exception\NoSupportedAlgorithmsException;
use phpseclib3\Net\SFTP;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -244,6 +245,52 @@ public function providing_an_invalid_password(): void
$provider->provideConnection();
}

/**
* @test
*/
public function retries_several_times_until_failure(): void
{
$connectivityChecker = new class implements ConnectivityChecker {
/** @var int */
public $calls = 0;

public function isConnected(SFTP $connection): bool
{
++$this->calls;

return $connection->isConnected();
}
};

$managesConnectionToxics = ToxiproxyManagement::forServer();
$managesConnectionToxics->resetPeerOnRequest('sftp', 10);

$maxTries = 5;

$provider = SftpConnectionProvider::fromArray(
[
'host' => 'localhost',
'username' => 'bar',
'privateKey' => __DIR__ . '/../../test_files/sftp/id_rsa',
'passphrase' => 'secret',
'port' => 8222,
'maxTries' => $maxTries,
'timeout' => 1,
'connectivityChecker' => $connectivityChecker,
]
);

$this->expectException(UnableToConnectToSftpHost::class);

try {
$provider->provideConnection();
} finally {
$managesConnectionToxics->removeAllToxics();

self::assertSame($maxTries + 1, $connectivityChecker->calls);
}
}

/**
* @test
*/
Expand Down
20 changes: 20 additions & 0 deletions test_files/toxiproxy/toxiproxy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"name": "sftp",
"listen": "[::]:8222",
"upstream": "sftp:22",
"enabled": true
},
{
"name": "ftp",
"listen": "[::]:8121",
"upstream": "ftp:21",
"enabled": true
},
{
"name": "ftpd",
"listen": "[::]:8122",
"upstream": "ftpd:21",
"enabled": true
}
]

0 comments on commit b4b33ab

Please sign in to comment.