‪TYPO3CMS  10.4
ExternalLinktypeTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use GuzzleHttp\Cookie\CookieJar;
21 use GuzzleHttp\Exception\ClientException;
22 use GuzzleHttp\Psr7\Response;
23 use Prophecy\Argument;
24 use Prophecy\Prophecy\ObjectProphecy;
30 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
31 
32 class ‪ExternalLinktypeTest extends UnitTestCase
33 {
34  protected function ‪setUp(): void
35  {
36  parent::setUp();
37  ‪$GLOBALS['LANG'] = $this->‪buildLanguageServiceProphecy()->reveal();
38  }
39 
40  private function ‪buildLanguageServiceProphecy(): ObjectProphecy
41  {
42  $languageServiceProphecy = $this->prophesize(LanguageService::class);
43  $languageServiceProphecy
44  ->includeLLFile('EXT:linkvalidator/Resources/Private/Language/Module/locallang.xlf');
45  $languageServiceProphecy->getLL(Argument::any())->willReturn('translation string');
46  return $languageServiceProphecy;
47  }
48 
53  {
54  $responseProphecy = $this->prophesize(Response::class);
55  $responseProphecy->getStatusCode()->willReturn(404);
56 
57  $exceptionProphecy = $this->prophesize(ClientException::class);
58  $exceptionProphecy->hasResponse()
59  ->willReturn(true);
60  $exceptionProphecy->getResponse()
61  ->willReturn($responseProphecy->reveal());
62 
63  $url = 'https://example.org/~not-existing-url';
64  $options = $this->‪getRequestHeaderOptions();
65  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
66  $requestFactoryProphecy->request($url, 'HEAD', $options)
67  ->willThrow($exceptionProphecy->reveal());
68 
69  $optionsSecondTryWithGET = array_merge_recursive($options, ['headers' => ['Range' => 'bytes=0-4048']]);
70  $requestFactoryProphecy->request($url, 'GET', $optionsSecondTryWithGET)
71  ->willThrow($exceptionProphecy->reveal());
72  $subject = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
73 
74  $result = $subject->checkLink($url, null, null);
75 
76  self::assertFalse($result);
77  }
78 
83  {
84  $responseProphecy = $this->prophesize(Response::class);
85  $responseProphecy->getStatusCode()->willReturn(404);
86 
87  $exceptionProphecy = $this->prophesize(ClientException::class);
88  $exceptionProphecy->hasResponse()
89  ->willReturn(true);
90  $exceptionProphecy->getResponse()
91  ->willReturn($responseProphecy->reveal());
92 
93  $options = $this->‪getRequestHeaderOptions();
94 
95  $url = 'https://example.org/~not-existing-url';
96  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
97  $requestFactoryProphecy->request($url, 'HEAD', $options)
98  ->willThrow($exceptionProphecy->reveal());
99  $optionsSecondTryWithGET = array_merge_recursive($options, ['headers' => ['Range' => 'bytes=0-4048']]);
100  $requestFactoryProphecy->request($url, 'GET', $optionsSecondTryWithGET)
101  ->willThrow($exceptionProphecy->reveal());
102  $subject = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
103 
104  $subject->checkLink($url, null, null);
105  $result = $subject->getErrorParams()['errorType'];
106 
107  self::assertSame(404, $result);
108  }
109 
110  private function ‪getCookieJarProphecy(): CookieJar
111  {
112  $cookieJar = $this->prophesize(CookieJar::class);
113  $cookieJar = $cookieJar->reveal();
114  GeneralUtility::addInstance(CookieJar::class, $cookieJar);
115  return $cookieJar;
116  }
117 
118  private function ‪getRequestHeaderOptions(): array
119  {
120  return [
121  'cookies' => $this->‪getCookieJarProphecy(),
122  'allow_redirects' => ['strict' => true],
123  'headers' => [
124  'User-Agent' => 'TYPO3 linkvalidator',
125  'Accept' => '*/*',
126  'Accept-Language' => '*',
127  'Accept-Encoding' => '*'
128  ]
129  ];
130  }
131 
133  {
134  // regression test for issue #92230: handle incomplete or faulty URLs gracefully
135  yield 'faulty URL with mailto' => [
136  'mailto:http://example.org',
137  'mailto:http://example.org'
138  ];
139  yield 'Relative URL' => [
140  '/abc',
141  '/abc'
142  ];
143 
144  // regression tests for issues #89488, #89682
145  yield 'URL with query parameter and ampersand' => [
146  'https://standards.cen.eu/dyn/www/f?p=204:6:0::::FSP_ORG_ID,FSP_LANG_ID:,22&cs=1A3FFBC44FAB6B2A181C9525249C3A829',
147  'https://standards.cen.eu/dyn/www/f?p=204:6:0::::FSP_ORG_ID,FSP_LANG_ID:,22&cs=1A3FFBC44FAB6B2A181C9525249C3A829'
148  ];
149  yield 'URL with query parameter and ampersand with HTML entities' => [
150  'https://standards.cen.eu/dyn/www/f?p=204:6:0::::FSP_ORG_ID,FSP_LANG_ID:,22&amp;cs=1A3FFBC44FAB6B2A181C9525249C3A829',
151  'https://standards.cen.eu/dyn/www/f?p=204:6:0::::FSP_ORG_ID,FSP_LANG_ID:,22&cs=1A3FFBC44FAB6B2A181C9525249C3A829'
152  ];
153 
154  // regression tests for #89378
155  yield 'URL with path with dashes' => [
156  'https://example.com/Unternehmen/Ausbildung-Qualifikation/Weiterbildung-in-Niedersachsen/',
157  'https://example.com/Unternehmen/Ausbildung-Qualifikation/Weiterbildung-in-Niedersachsen/'
158  ];
159  yield 'URL with path with dashes (2)' => [
160  'https://example.com/startseite/wirtschaft/wirtschaftsfoerderung/beratung-foerderung/gruenderberatung/gruenderforen.html',
161  'https://example.com/startseite/wirtschaft/wirtschaftsfoerderung/beratung-foerderung/gruenderberatung/gruenderforen.html'
162  ];
163  yield 'URL with path with dashes (3)' => [
164  'http://example.com/universitaet/die-uni-im-ueberblick/lageplan/gebaeude/building/120',
165  'http://example.com/universitaet/die-uni-im-ueberblick/lageplan/gebaeude/building/120'
166  ];
167  yield 'URL with path and query parameters (including &, ~,; etc.)' => [
168  'http://example.com/tv?bcpid=1701167454001&amp;amp;amp;bckey=AQ~~,AAAAAGL7LqU~,aXlKNnCf9d9Tmck-kOc4PGFfCgHjM5JR&amp;amp;amp;bctid=1040702768001',
169  'http://example.com/tv?bcpid=1701167454001&amp;amp;bckey=AQ~~,AAAAAGL7LqU~,aXlKNnCf9d9Tmck-kOc4PGFfCgHjM5JR&amp;amp;bctid=1040702768001'
170  ];
171 
172  // make sure we correctly handle URLs with query parameters and fragment etc.
173  yield 'URL with query parameters, fragment, user, pass, port etc.' => [
174  'http://usr:pss@example.com:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment',
175  'http://usr:pss@example.com:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment'
176  ];
177  yield 'domain with special characters, URL with query parameters, fragment, user, pass, port etc.' => [
178  'http://usr:pss@äxample.com:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment',
179  'http://usr:pss@xn--xample-9ta.com:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment'
180  ];
181 
182  // domains with special characters: should be converted to punycode
183  yield 'domain with special characters' => [
184  'https://www.grün-example.org',
185  'https://www.xn--grn-example-uhb.org'
186  ];
187  yield 'domain with special characters and path' => [
188  'https://www.grün-example.org/a/bcd-efg/sfsfsfsfsf',
189  'https://www.xn--grn-example-uhb.org/a/bcd-efg/sfsfsfsfsf'
190  ];
191  }
192 
197  public function ‪preprocessUrlReturnsCorrectString(string $inputUrl, $expectedResult)
198  {
199  $subject = new ‪ExternalLinktype();
200  $method = new \ReflectionMethod($subject, 'preprocessUrl');
201  $method->setAccessible(true);
202  $result = $method->invokeArgs($subject, [$inputUrl]);
203  self::assertEquals($result, $expectedResult);
204  }
205 
209  public function ‪setAdditionalConfigMergesHeaders(): void
210  {
211  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
212  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::any())->shouldBeCalled();
213 
214  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
215  $externalLinktype->setAdditionalConfig(['headers.' => [
216  'X-MAS' => 'Merry!'
217  ]]);
218 
219  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
220 
221  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::that(function ($result) {
222  if ($result['headers']['X-MAS'] === 'Merry!' && $result['headers']['User-Agent'] === 'TYPO3 linkvalidator') {
223  return true;
224  }
225  return false;
226  }))->shouldBeCalled();
227  }
228 
235  {
236  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
237  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
238  $externalLinktype->setAdditionalConfig([]);
239  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::that(function ($result) {
240  if (isset($result['timeout'])) {
241  return false;
242  }
243  return true;
244  }))->shouldBeCalled();
245  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
246  }
247 
252  {
253  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
254  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::any())->shouldBeCalled();
255 
256  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
257  $externalLinktype->setAdditionalConfig([
258  'httpAgentName' => 'TYPO3 Testing'
259  ]);
260 
261  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
262 
263  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::that(function ($result) {
264  if ($result['headers']['User-Agent'] === 'TYPO3 Testing') {
265  return true;
266  }
267  return false;
268  }))->shouldBeCalled();
269  }
270 
275  {
276  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
277  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::any())->shouldBeCalled();
278 
279  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
280  $externalLinktype->setAdditionalConfig([
281  'httpAgentUrl' => 'http://example.com'
282  ]);
283 
284  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
285 
286  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::that(function ($result) {
287  if ($result['headers']['User-Agent'] === 'TYPO3 linkvalidator http://example.com') {
288  return true;
289  }
290  return false;
291  }))->shouldBeCalled();
292  }
293 
298  {
299  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
300  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::any())->shouldBeCalled();
301 
302  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
303  $externalLinktype->setAdditionalConfig([
304  'httpAgentEmail' => 'mail@example.com'
305  ]);
306 
307  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
308 
309  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::that(function ($result) {
310  if ($result['headers']['User-Agent'] === 'TYPO3 linkvalidator;mail@example.com') {
311  return true;
312  }
313  return false;
314  }))->shouldBeCalled();
315  }
316 
321  {
322  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
323  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::any())->shouldBeCalled();
324  ‪$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'] = 'test@example.com';
325 
326  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
327  $externalLinktype->setAdditionalConfig([]);
328 
329  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
330 
331  $requestFactoryProphecy->request('http://example.com', 'HEAD', Argument::that(function ($result) {
332  if ($result['headers']['User-Agent'] === 'TYPO3 linkvalidator;test@example.com') {
333  return true;
334  }
335  return false;
336  }))->shouldBeCalled();
337  }
338 
343  {
344  $requestFactoryProphecy = $this->prophesize(RequestFactory::class);
345  $requestFactoryProphecy->request('http://example.com', 'GET', Argument::any())->shouldBeCalled();
346  ‪$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'] = 'test@example.com';
347 
348  $externalLinktype = new ‪ExternalLinktype($requestFactoryProphecy->reveal());
349  $externalLinktype->setAdditionalConfig([
350  'method' => 'GET',
351  'range' => '0-2048'
352  ]);
353 
354  $externalLinktype->checkLink('http://example.com', [], $this->prophesize(LinkAnalyzer::class)->reveal());
355 
356  $requestFactoryProphecy->request('http://example.com', 'GET', Argument::that(function ($result) {
357  if ($result['headers']['Range'] === 'bytes=0-2048') {
358  return true;
359  }
360  return false;
361  }))->shouldBeCalled();
362  }
363 }
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setAdditionalConfigMergesHeaders
‪setAdditionalConfigMergesHeaders()
Definition: ExternalLinktypeTest.php:209
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\preprocessUrlsDataProvider
‪preprocessUrlsDataProvider()
Definition: ExternalLinktypeTest.php:132
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setAdditionalConfigOverwritesUserAgent
‪setAdditionalConfigOverwritesUserAgent()
Definition: ExternalLinktypeTest.php:251
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\checkLinkWithExternalUrlNotFoundResultsNotFoundErrorType
‪checkLinkWithExternalUrlNotFoundResultsNotFoundErrorType()
Definition: ExternalLinktypeTest.php:82
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\requestWithNoTimeoutIsCalledIfTimeoutNotSetByTsConfig
‪requestWithNoTimeoutIsCalledIfTimeoutNotSetByTsConfig()
Definition: ExternalLinktypeTest.php:234
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\preprocessUrlReturnsCorrectString
‪preprocessUrlReturnsCorrectString(string $inputUrl, $expectedResult)
Definition: ExternalLinktypeTest.php:197
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setUp
‪setUp()
Definition: ExternalLinktypeTest.php:34
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest
Definition: ExternalLinktypeTest.php:33
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype
Definition: ExternalLinktypeTest.php:18
‪TYPO3\CMS\Linkvalidator\Linktype\ExternalLinktype
Definition: ExternalLinktype.php:30
‪TYPO3\CMS\Core\Http\RequestFactory
Definition: RequestFactory.php:31
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setAdditionalConfigAppendsEmailFromGlobalsIfConfigured
‪setAdditionalConfigAppendsEmailFromGlobalsIfConfigured()
Definition: ExternalLinktypeTest.php:320
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setAdditionalConfigAppendsEmailIfConfigured
‪setAdditionalConfigAppendsEmailIfConfigured()
Definition: ExternalLinktypeTest.php:297
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setAdditionalConfigAppendsAgentUrlIfConfigured
‪setAdditionalConfigAppendsAgentUrlIfConfigured()
Definition: ExternalLinktypeTest.php:274
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\buildLanguageServiceProphecy
‪buildLanguageServiceProphecy()
Definition: ExternalLinktypeTest.php:40
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\getRequestHeaderOptions
‪getRequestHeaderOptions()
Definition: ExternalLinktypeTest.php:118
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\setAdditionalConfigSetsRangeAndMethod
‪setAdditionalConfigSetsRangeAndMethod()
Definition: ExternalLinktypeTest.php:342
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\checkLinkWithExternalUrlNotFoundReturnsFalse
‪checkLinkWithExternalUrlNotFoundReturnsFalse()
Definition: ExternalLinktypeTest.php:52
‪TYPO3\CMS\Linkvalidator\Tests\Unit\Linktype\ExternalLinktypeTest\getCookieJarProphecy
‪getCookieJarProphecy()
Definition: ExternalLinktypeTest.php:110