3 namespace Drupal\aggregator\Plugin\aggregator\fetcher;
5 use Drupal\aggregator\Plugin\FetcherInterface;
6 use Drupal\aggregator\FeedInterface;
7 use Drupal\Component\Datetime\DateTimePlus;
8 use Drupal\Core\Http\ClientFactory;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use GuzzleHttp\Exception\RequestException;
11 use GuzzleHttp\Psr7\Request;
12 use Psr\Http\Message\RequestInterface;
13 use Psr\Http\Message\ResponseInterface;
14 use Psr\Http\Message\UriInterface;
15 use Psr\Log\LoggerInterface;
16 use Symfony\Component\DependencyInjection\ContainerInterface;
19 * Defines a default fetcher implementation.
21 * Uses the http_client service to download the feed.
25 * title = @Translation("Default fetcher"),
26 * description = @Translation("Downloads data from a URL using Drupal's HTTP request handler.")
29 class DefaultFetcher implements FetcherInterface, ContainerFactoryPluginInterface {
32 * The HTTP client to fetch the feed data with.
34 * @var \Drupal\Core\Http\ClientFactory
36 protected $httpClientFactory;
41 * @var \Psr\Log\LoggerInterface
46 * Constructs a DefaultFetcher object.
48 * @param \Drupal\Core\Http\ClientFactory $http_client_factory
49 * A Guzzle client object.
50 * @param \Psr\Log\LoggerInterface $logger
53 public function __construct(ClientFactory $http_client_factory, LoggerInterface $logger) {
54 $this->httpClientFactory = $http_client_factory;
55 $this->logger = $logger;
61 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
63 $container->get('http_client_factory'),
64 $container->get('logger.factory')->get('aggregator')
71 public function fetch(FeedInterface $feed) {
72 $request = new Request('GET', $feed->getUrl());
73 $feed->source_string = FALSE;
75 // Generate conditional GET headers.
76 if ($feed->getEtag()) {
77 $request = $request->withAddedHeader('If-None-Match', $feed->getEtag());
79 if ($feed->getLastModified()) {
80 $request = $request->withAddedHeader('If-Modified-Since', gmdate(DateTimePlus::RFC7231, $feed->getLastModified()));
85 /** @var \Psr\Http\Message\UriInterface $actual_uri */
87 $response = $this->httpClientFactory->fromOptions(['allow_redirects' => [
88 'on_redirect' => function(RequestInterface $request, ResponseInterface $response, UriInterface $uri) use (&$actual_uri) {
89 $actual_uri = (string) $uri;
93 // In case of a 304 Not Modified, there is no new content, so return
95 if ($response->getStatusCode() == 304) {
99 $feed->source_string = (string) $response->getBody();
100 if ($response->hasHeader('ETag')) {
101 $feed->setEtag($response->getHeaderLine('ETag'));
103 if ($response->hasHeader('Last-Modified')) {
104 $feed->setLastModified(strtotime($response->getHeaderLine('Last-Modified')));
106 $feed->http_headers = $response->getHeaders();
108 // Update the feed URL in case of a 301 redirect.
109 if ($actual_uri && $actual_uri !== $feed->getUrl()) {
110 $feed->setUrl($actual_uri);
114 catch (RequestException $e) {
115 $this->logger->warning('The feed from %site seems to be broken because of error "%error".', ['%site' => $feed->label(), '%error' => $e->getMessage()]);
116 drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', ['%site' => $feed->label(), '%error' => $e->getMessage()]), 'warning');