Passed
Push — master ( f2b4f1...f4fa9e )
by Dispositif
03:37
created

TorClientAdapter::validateIpOrException()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 4
dl 0
loc 7
rs 10
c 1
b 0
f 1
cc 2
nc 2
nop 0
1
<?php
2
/*
3
 * This file is part of dispositif/wikibot application (@github)
4
 * 2019-2023 © Philippe M./Irønie  <[email protected]>
5
 * For the full copyright and MIT license information, view the license file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Infrastructure;
11
12
use App\Application\InfrastructurePorts\HttpClientInterface;
13
use App\Domain\Exceptions\ConfigException;
14
use Exception;
15
use GuzzleHttp\Client;
16
use GuzzleHttp\Handler\CurlHandler;
17
use GuzzleHttp\HandlerStack;
18
use GuzzleTor\Middleware;
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\UriInterface;
21
22
/**
23
 * TODO : param 'http_errors' for exception or not behind Tor :)
24
 * TODO : options for user-agent
25
 * Lib megahertz/guzzle-tor : https://github.com/megahertz/guzzle-tor/tree/master
26
 */
27
class TorClientAdapter extends GuzzleClientAdapter implements HttpClientInterface
28
{
29
    public const FAKE_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36";
30
    protected const API_GET_IP = 'https://api64.ipify.org';
31
32
    protected const DEFAULT_MAX_REDIRECTS = 5;
33
    protected int $maxRedirects = 0;
34
35
    public function __construct(array $options = [])
36
    {
37
        $proxy = getenv('TOR_PROXY');
38
        $torControl = getenv('TOR_CONTROL');
39
        if (!$proxy || !$torControl) {
40
            throw new ConfigException('TOR proxy or control not defined in .env');
41
        }
42
43
        $stack = new HandlerStack();
44
        $stack->setHandler(new CurlHandler());
45
        $stack->push(Middleware::tor($proxy, $torControl));
46
47
        $this->client = new Client([
48
            'handler' => $stack,
49
            'timeout' => $options['timeout'] ?? 20,
50
            'allow_redirects' => $options['allow_redirects'] ?? true,
51
            'headers' => $options['headers'] ?? ['User-Agent' => getenv('USER_AGENT')],
52
            'verify' => false,
53
            // 'http_errors' => false, // no Exception on 4xx 5xx
54
        ]);
55
56
        $this->validateIpOrException();
57
    }
58
59
    /**
60
     * @throws Exception
61
     */
62
    protected function validateIpOrException(): void
63
    {
64
        $torIp = $this->getIp();
65
        if (!$torIp) {
66
            throw new Exception('TOR IP not found');
67
        }
68
        echo "TOR IP : $torIp \n";
69
    }
70
71
    public function getIp(): ?string
72
    {
73
        $response = $this->client->get(self::API_GET_IP, [
74
            'timeout' => 10,
75
            'headers' => [
76
                'User-Agent' => self::FAKE_USER_AGENT,
77
            ],
78
            'verify' => false, // CURLOPT_SSL_VERIFYHOST
79
            'http_errors' => false, // no Exception on 4xx 5xx
80
        ]);
81
82
        if ($response->getStatusCode() === 200) {
83
            return $response->getBody()->getContents();
84
        }
85
86
        return null;
87
    }
88
89
    public function get(string|UriInterface $uri, array $options = []): ResponseInterface
90
    {
91
        if (isset($options['allow_redirects']) && $options['allow_redirects'] !== false) {
92
            $this->maxRedirects = self::DEFAULT_MAX_REDIRECTS;
93
        }
94
95
        return $this->getRecursive($uri, $options);
96
    }
97
98
    /**
99
     * todo : add redirect http referer
100
     */
101
    private function getRecursive(UriInterface|string $uri, array $options, int $loop = 0): ResponseInterface
102
    {
103
        $response = $this->client->get($uri, $options);
104
105
        // Redirect 3xx
106
        if ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
107
            $redirectUri = $response->getHeader('location')[0] ?? null;
108
            if ($loop >= $this->maxRedirects || !$redirectUri) {
109
                throw new Exception('TorClientAdapter::get Error too many redirects ' . $response->getStatusCode());
110
            }
111
            $loop++;
112
            return $this->getRecursive($redirectUri, $options, $loop);
113
        }
114
115
        // Error 4xx 5xx
116
        if ($response->getStatusCode() >= 400) {
117
            throw new Exception($response->getStatusCode() . ' ' . $response->getReasonPhrase());
118
        }
119
120
        return $response;
121
    }
122
123
    /**
124
     * @deprecated use get() or implement. Trying to HEAD or POST with Tor ?!
125
     */
126
    public function request($method, $uri, array $options = []): ResponseInterface
127
    {
128
        throw new Exception('NOT YET IMPLEMENTED z944');
129
    }
130
}
131