|
3 | 3 | namespace SyntaxPhoenix\Api\BGPView; |
4 | 4 |
|
5 | 5 | use GuzzleHttp\Client; |
6 | | -use Psr\Http\Message\ResponseInterface; |
| 6 | +use Nette\Caching\Cache; |
| 7 | +use Nette\Caching\Storage; |
| 8 | +use Nette\Caching\Storages\FileStorage; |
7 | 9 | use SyntaxPhoenix\Api\BGPView\Exceptions\RequestFailedException; |
8 | 10 |
|
9 | 11 | class BGPView |
10 | 12 | { |
11 | 13 |
|
12 | 14 | private string $baseUrl; |
| 15 | + private bool $caching; |
13 | 16 | private Client $client; |
| 17 | + private Storage $storage; |
| 18 | + private Cache $cache; |
| 19 | + private string $cacheTime; |
| 20 | + private float $lastRequestTimestamp = 0; |
| 21 | + private float $requestTimeout; |
14 | 22 |
|
15 | | - public function __construct(string $baseUrl) { |
| 23 | + public function __construct(string $baseUrl, float $requestTimeout = 0.4, bool $caching = false, ?string $cachingUrl = 'web/cache/bgpview', ?string $cacheTime = '10 minutes') { |
| 24 | + if ($caching) { |
| 25 | + $this->createPath($cachingUrl); |
| 26 | + $this->storage = new FileStorage($cachingUrl); |
| 27 | + $this->cache = new Cache($this->storage, 'bgpview-requests'); |
| 28 | + } |
16 | 29 | $this->baseUrl = $baseUrl; |
| 30 | + $this->caching = $caching; |
| 31 | + $this->cacheTime = $cacheTime; |
| 32 | + $this->requestTimeout = $requestTimeout; |
17 | 33 | $this->client = new Client([ |
18 | | - 'base_uri' => $baseUrl, |
19 | | - 'timeout' => 2.0, |
| 34 | + 'base_uri' => $baseUrl |
20 | 35 | ]); |
21 | 36 | } |
22 | 37 |
|
23 | 38 | public function getAsnDetails(int $asNumber): array |
24 | 39 | { |
25 | | - return json_decode($this->getDataByApi('asn/' . $asNumber)->getBody(), true)['data']; |
| 40 | + return $this->getDataByApi('asn/' . $asNumber)['data']; |
26 | 41 | } |
27 | 42 |
|
28 | 43 | public function getAsnPrefixes(int $asNumber): array |
29 | 44 | { |
30 | | - return json_decode($this->getDataByApi('asn/' . $asNumber . '/prefixes')->getBody(), true)['data']; |
| 45 | + return $this->getDataByApi('asn/' . $asNumber . '/prefixes')['data']; |
31 | 46 | } |
32 | 47 |
|
33 | 48 | public function getAsnPeers(int $asNumber): array |
34 | 49 | { |
35 | | - return json_decode($this->getDataByApi('asn/' . $asNumber . '/peers')->getBody(), true)['data']; |
| 50 | + return $this->getDataByApi('asn/' . $asNumber . '/peers')['data']; |
36 | 51 | } |
37 | 52 |
|
38 | 53 | public function getAsnUpstreams(int $asNumber): array |
39 | 54 | { |
40 | | - return json_decode($this->getDataByApi('asn/' . $asNumber . '/upstreams')->getBody(), true)['data']; |
| 55 | + return $this->getDataByApi('asn/' . $asNumber . '/upstreams')['data']; |
41 | 56 | } |
42 | 57 |
|
43 | 58 | public function getAsnDownstreams(int $asNumber): array |
44 | 59 | { |
45 | | - return json_decode($this->getDataByApi('asn/' . $asNumber . '/downstreams')->getBody(), true)['data']; |
| 60 | + return $this->getDataByApi('asn/' . $asNumber . '/downstreams')['data']; |
46 | 61 | } |
47 | 62 |
|
48 | 63 | public function getPrefix(string $ipAddress, int $cidr): array |
49 | 64 | { |
50 | | - return json_decode($this->getDataByApi('prefix/' . $ipAddress . '/' . $cidr)->getBody(), true)['data']; |
| 65 | + return $this->getDataByApi('prefix/' . $ipAddress . '/' . $cidr)['data']; |
51 | 66 | } |
52 | 67 |
|
53 | 68 | public function getIPDetails(string $ipAddress): array |
54 | 69 | { |
55 | | - return json_decode($this->getDataByApi('prefix/ip/' . $ipAddress)->getBody(), true)['data']; |
| 70 | + return $this->getDataByApi('ip/' . $ipAddress)['data']; |
56 | 71 | } |
57 | 72 |
|
58 | 73 | public function getIXDetails(int $ixId): array |
59 | 74 | { |
60 | | - return json_decode($this->getDataByApi('ix/' . $ixId)->getBody(), true)['data']; |
| 75 | + return $this->getDataByApi('ix/' . $ixId)['data']; |
61 | 76 | } |
62 | 77 |
|
63 | | - private function getDataByApi(string $urlPart): ResponseInterface |
| 78 | + private function getDataByApi(string $urlPart): array |
64 | 79 | { |
| 80 | + if ($this->caching) { |
| 81 | + $response = $this->cache->load($urlPart); |
| 82 | + if ($response) { |
| 83 | + return $response; |
| 84 | + } |
| 85 | + } |
| 86 | + $time = microtime(true); |
| 87 | + if (($this->lastRequestTimestamp + $this->requestTimeout) > $time) { |
| 88 | + $restTime = $this->requestTimeout - ($time - $this->lastRequestTimestamp); |
| 89 | + usleep($restTime * 1000 * 1000); |
| 90 | + } |
65 | 91 | $response = $this->client->request('GET', $urlPart); |
66 | 92 | if ($response->getStatusCode() != 200) { |
67 | 93 | throw new RequestFailedException(); |
68 | 94 | } |
69 | | - return $response; |
| 95 | + $finalResponse = json_decode($response->getBody(), true); |
| 96 | + if ($this->caching) { |
| 97 | + $this->cache->save($urlPart, $finalResponse, [ |
| 98 | + Cache::EXPIRE => $this->cacheTime ?? '10 minutes', |
| 99 | + ]); |
| 100 | + } |
| 101 | + $this->lastRequestTimestamp = microtime(true); |
| 102 | + |
| 103 | + return $finalResponse; |
| 104 | + } |
| 105 | + |
| 106 | + private function createPath($path) { |
| 107 | + if (is_dir($path)) { |
| 108 | + return true; |
| 109 | + } |
| 110 | + $prev_path = substr($path, 0, strrpos($path, '/', -2) + 1 ); |
| 111 | + $return = $this->createPath($prev_path); |
| 112 | + return ($return && is_writable($prev_path)) ? mkdir($path) : false; |
70 | 113 | } |
71 | 114 | } |
0 commit comments