/
RingdownCurlHttpClient.php
114 lines (93 loc) · 3.27 KB
/
RingdownCurlHttpClient.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<?php declare(strict_types=1);
/*
* This file is part of Phive.
*
* Copyright (c) Arne Blankerts <arne@blankerts.de>, Sebastian Heuer <sebastian@phpeople.de> and contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/
namespace PharIo\Phive;
use const CURLE_COULDNT_RESOLVE_HOST;
use const DNS_A;
use const DNS_AAAA;
use function array_merge;
use function array_shift;
use function count;
use function dns_get_record;
use function sprintf;
use Exception;
use PharIo\Phive\Cli\Output;
class RingdownCurlHttpClient implements HttpClient {
/** @var HttpClient */
private $client;
/** @var CurlConfig */
private $config;
/** @var Output */
private $output;
/** @var array */
private $resolved = [];
public function __construct(HttpClient $client, CurlConfig $config, Output $output) {
$this->client = $client;
$this->config = $config;
$this->output = $output;
}
public function get(Url $url, ?ETag $etag = null): HttpResponse {
return $this->execWrapper('get', $url, $etag);
}
public function head(Url $url, ?ETag $etag = null): HttpResponse {
return $this->execWrapper('head', $url, $etag);
}
private function execWrapper(string $method, Url $url, ?ETag $etag): HttpResponse {
$hostname = $url->getHostname();
$response = null;
foreach ($this->resolveHostname($hostname) as $ip) {
$this->config->setResolvedIp($hostname, $ip);
$this->output->writeInfo(sprintf('Trying to connect to %s (%s)', $hostname, $ip));
try {
$response = $this->client->{$method}($url, $etag);
/** @var HttpResponse $response */
if ($response->isSuccess()) {
return $response;
}
} catch (HttpException $e) {
$this->output->writeError(sprintf('Request failed: %s', $e->getMessage()));
}
$this->removeUnavailable($hostname);
}
if ($response === null) {
throw new HttpException('No mirror yielded any result. Giving up.');
}
return $response;
}
private function resolveHostname(string $hostname): array {
if (!isset($this->resolved[$hostname])) {
$ipList = array_merge(
$this->queryDNS($hostname, DNS_A),
$this->queryDNS($hostname, DNS_AAAA)
);
if (!count($ipList)) {
throw new HttpException(
sprintf('DNS Problem: Did not find any IP for hostname "%s"', $hostname),
CURLE_COULDNT_RESOLVE_HOST
);
}
$this->resolved[$hostname] = $ipList;
}
return $this->resolved[$hostname];
}
private function queryDNS(string $hostname, int $type): array {
$ipList = [];
try {
foreach (dns_get_record($hostname, $type) as $result) {
$ipList[] = $result[$type === DNS_A ? 'ip' : 'ipv6'];
}
} catch (Exception $e) {
}
return $ipList;
}
private function removeUnavailable(string $hostname): void {
array_shift($this->resolved[$hostname]);
}
}