ProxyController.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. namespace App\Controller;
  3. use PhpParser\Node\Stmt\Throw_;
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\Cache\Adapter\FilesystemAdapter;
  6. use Symfony\Component\HttpClient\Exception\TimeoutException;
  7. use Symfony\Component\HttpClient\HttpClient;
  8. use Symfony\Component\HttpFoundation\Response;
  9. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  10. use Symfony\Component\Routing\Annotation\Route;
  11. use Symfony\Contracts\Cache\CacheInterface;
  12. use Symfony\Contracts\Cache\ItemInterface;
  13. class ProxyController extends AbstractController
  14. {
  15. private $cache;
  16. private $client;
  17. private $expiration = 3600 * 2;
  18. private $tryAfter = 1800 * 2;
  19. public function __construct(CacheInterface $cache)
  20. {
  21. $this->cache = $cache;
  22. $this->client = HttpClient::create();
  23. }
  24. /**
  25. * @Route("/proxy/{type}/{name}", name="proxy")
  26. */
  27. public function proxy(string $type, string $name): Response {
  28. $valid = [
  29. 'kategorier',
  30. 'malgruppe',
  31. 'program',
  32. ];
  33. if (!in_array($type, $valid))
  34. throw new NotFoundHttpException(sprintf("Invalid type «%s»", $type));
  35. $key = sha1(sprintf('%s/%s', $type, $name));
  36. $item = $this->cache->getItem($key);
  37. if ($type == 'program')
  38. $callback = function () use ($name) {
  39. return $this->client->request(
  40. 'GET',
  41. sprintf('https://tromsotid.no/program%s',
  42. base64_decode($name . '==')
  43. ), [
  44. 'max_duration' => 3
  45. ]
  46. );
  47. };
  48. else
  49. $callback = function () use ($type, $name) {
  50. return $this->client->request(
  51. 'GET',
  52. sprintf('https://tromsotid.no/%s/%s', $type, $name), [
  53. 'max_duration' => 3
  54. ]
  55. );
  56. };
  57. if ($item->isHit()) {
  58. $data = $item->get();
  59. if ((time() - $data['last_update']) > $this->tryAfter) {
  60. $this->updateCache($item, $callback);
  61. }
  62. } else {
  63. $this->updateCache($item, $callback);
  64. }
  65. if (!isset($item->get()['data']))
  66. throw new TimeoutException("TimeoutException");
  67. /*
  68. if ($type == 'program') {
  69. dump($type, $name, base64_decode($name . '=='));
  70. return new Response(htmlentities($item->get()['data']));
  71. }
  72. */
  73. return new Response(($item->get()['data']));
  74. }
  75. private function updateCache(ItemInterface $item, $callback, int $retry = 5): ?ItemInterface {
  76. try {
  77. $resp = $callback();
  78. if ($resp->getStatusCode() != 200)
  79. throw new \Exception("Failed with status code " + $resp->getStatusCode());
  80. $item->set([
  81. 'last_update' => time(),
  82. 'data' => $resp->getContent()
  83. ]);
  84. } catch (\Exception $e) {
  85. if ($retry > 0) {
  86. return $this->updateCache($item, $callback, $retry - 1);
  87. }
  88. }
  89. if (isset($item->get()['data'])) {
  90. $item->expiresAfter($this->expiration);
  91. $this->cache->save($item);
  92. }
  93. return $item;
  94. }
  95. }