*/ public static function addresses(): iterable { yield 'www geht auf die nackte Domain, mit Pfad und Abfrage' => ['https://example.de', 'https://www.example.de/seite/unter?x=1&y=2', 'https://example.de/seite/unter?x=1&y=2']; yield 'die nackte Domain geht auf www' => ['https://www.example.de', 'https://example.de/', 'https://www.example.de/']; yield 'das Schema kommt aus SITE_ORIGIN, nicht aus der Anfrage' => ['https://example.de', 'http://www.example.de/kontakt', 'https://example.de/kontakt']; yield 'Gross- und Kleinschreibung des Hosts spielt keine Rolle' => ['https://example.de', 'https://WWW.Example.DE/', 'https://example.de/']; yield 'ein Port in SITE_ORIGIN bleibt erhalten' => ['https://example.de:8443', 'https://www.example.de/x', 'https://example.de:8443/x']; yield 'auch die Verwaltung wechselt den Host - ein Host, ein Sitzungscookie' => ['https://example.de', 'https://www.example.de/admin/login', 'https://example.de/admin/login']; yield 'ein abschliessender Schraegstrich in SITE_ORIGIN stoert nicht' => ['https://example.de/', 'https://www.example.de/a', 'https://example.de/a']; yield 'auf der richtigen Adresse passiert nichts' => ['https://example.de', 'https://example.de/seite', null]; yield 'auf der richtigen Adresse mit www passiert nichts' => ['https://www.example.de', 'https://www.example.de/', null]; yield 'nur das Schema weicht ab: keine Umleitung, das macht der Webserver' => ['https://example.de', 'http://example.de/', null]; yield 'eine Subdomain ist kein www-Zwilling' => ['https://example.de', 'https://staging.example.de/', null]; yield 'eine fremde Domain bleibt erreichbar' => ['https://example.de', 'https://kunde123.hoster-vorschau.de/', null]; yield 'eine IP-Adresse bleibt erreichbar' => ['https://example.de', 'http://127.0.0.1/', null]; yield 'localhost bleibt erreichbar' => ['https://example.de', 'http://localhost:8080/', null]; yield 'die Entwicklungsadresse mit Port ist ihre eigene' => ['http://127.0.0.1:8080', 'http://127.0.0.1:8080/', null]; yield 'ein doppeltes www ist die www-Fassung der eingetragenen Adresse und kommt heim' => ['https://www.example.de', 'https://www.www.example.de/', 'https://www.example.de/']; yield 'ein doppeltes www ist kein Zwilling der nackten Domain' => ['https://example.de', 'https://www.www.example.de/', null]; yield 'ohne SITE_ORIGIN keine Umleitung' => ['', 'https://www.example.de/', null]; yield 'SITE_ORIGIN ohne Schema ist keine Adresse' => ['example.de', 'https://www.example.de/', null]; yield 'die Vorlage aus .env.example leitet niemanden um' => ['https://example.de', 'https://neue-website.de/install', null]; } #[DataProvider('addresses')] public function testTheOtherSpellingIsSentToSiteOrigin(string $origin, string $url, ?string $expected): void { $event = $this->event(Request::create($url)); (new CanonicalHostSubscriber($origin))->onRequest($event); $response = $event->getResponse(); if (null === $expected) { self::assertNull($response, 'Diese Anfrage darf nicht umgeleitet werden.'); return; } self::assertNotNull($response); self::assertSame(301, $response->getStatusCode(), 'Dauerhaft: Suchmaschinen sollen die Adresse übernehmen.'); self::assertSame($expected, $response->headers->get('Location')); self::assertStringContainsString('max-age=3600', (string) $response->headers->get('Cache-Control')); } public function testOnlyReadingRequestsAreRedirected(): void { foreach (['POST', 'PUT', 'DELETE'] as $method) { $event = $this->event(Request::create('https://www.example.de/formular/kontakt', $method)); (new CanonicalHostSubscriber('https://example.de'))->onRequest($event); self::assertNull($event->getResponse(), $method.': ein 301 würde die Eingabe verwerfen.'); } $event = $this->event(Request::create('https://www.example.de/', 'HEAD')); (new CanonicalHostSubscriber('https://example.de'))->onRequest($event); self::assertSame(301, $event->getResponse()?->getStatusCode()); } public function testSubRequestsAreLeftAlone(): void { $event = new RequestEvent($this->createMock(HttpKernelInterface::class), Request::create('https://www.example.de/'), HttpKernelInterface::SUB_REQUEST); (new CanonicalHostSubscriber('https://example.de'))->onRequest($event); self::assertNull($event->getResponse()); } public function testItAnswersBeforeMaintenanceAndInstaller(): void { $priority = CanonicalHostSubscriber::getSubscribedEvents()['kernel.request'][1]; self::assertGreaterThan(40, $priority, 'Vor dem Wartungszustand (40) und dem Installer (36): die Umleitung braucht keine Datenbank.'); self::assertLessThan(300, $priority, 'Nach den Kopfzeilen der Verwaltung (300), die keine Antwort setzen.'); } private function event(Request $request): RequestEvent { return new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); } }