Add VaryAcceptLanguage middleware (#5687)
authorTim Düsterhus <duesterhus@woltlab.com>
Fri, 13 Oct 2023 15:31:58 +0000 (17:31 +0200)
committerGitHub <noreply@github.com>
Fri, 13 Oct 2023 15:31:58 +0000 (17:31 +0200)
* Add VaryAcceptLanguage middleware

* Fix typos

---------

Co-authored-by: Alexander Ebert <ebert@woltlab.com>
wcfsetup/install/files/lib/http/middleware/VaryAcceptLanguage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/request/RequestHandler.class.php

diff --git a/wcfsetup/install/files/lib/http/middleware/VaryAcceptLanguage.class.php b/wcfsetup/install/files/lib/http/middleware/VaryAcceptLanguage.class.php
new file mode 100644 (file)
index 0000000..3b0b3f0
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+namespace wcf\http\middleware;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\MiddlewareInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+use wcf\http\LegacyPlaceholderResponse;
+use wcf\system\WCF;
+
+/**
+ * Adds 'vary: accept-language' to the response for guests.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+final class VaryAcceptLanguage implements MiddlewareInterface
+{
+    /**
+     * @inheritDoc
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        // Do not set the header with a regular `\header()` call, because:
+        // (a) Any 'vary' in a controller is likely going to override the header,
+        //     as $replace defaults to 'true', whereas 'vary' contains a *list*
+        //     of items.
+        // (b) For the same reason we cannot use `\header_remove()` to delegate
+        //     the logic to the PSR-7 response emitter, as we might also
+        //     remove unrelated items.
+        //
+        // This is different to the other middlewares, because they use `withHeader`,
+        // thus overriding any existing header, instead of `withAddedHeader` to
+        // add a single item.
+        //
+        // Furthermore adding the `vary: accept-language` is not super necessary,
+        // because caching for responses might already be disabled.
+        //
+        // Thus we attach it to any PSR-7 responses on a best effort basis, the number
+        // of controllers returning a PSR-7 response is expected to grow over time.
+
+        $response = $handler->handle($request);
+
+        if ($response instanceof LegacyPlaceholderResponse) {
+            return $response;
+        }
+
+        if (!WCF::getUser()->userID) {
+            return $response->withAddedHeader('vary', 'accept-language');
+        } else {
+            return $response;
+        }
+    }
+}
index b2050ef007c404e3292e43f4c7475c0bef8cd42c..ea52771780757150596cd52130dfc87db8f623d3 100644 (file)
@@ -29,6 +29,7 @@ use wcf\http\middleware\HandleValinorMappingErrors;
 use wcf\http\middleware\JsonBody;
 use wcf\http\middleware\PreventMimeSniffing;
 use wcf\http\middleware\TriggerBackgroundQueue;
+use wcf\http\middleware\VaryAcceptLanguage;
 use wcf\http\middleware\Xsrf;
 use wcf\http\Pipeline;
 use wcf\http\StaticResponseHandler;
@@ -128,6 +129,7 @@ final class RequestHandler extends SingletonFactory
                     new EnforceCacheControlPrivate(),
                     new EnforceNoCacheForTemporaryRedirects(),
                     new EnforceFrameOptions(),
+                    new VaryAcceptLanguage(),
                     new CheckHttpMethod(),
                     new Xsrf(),
                     new CheckSystemEnvironment(),
@@ -157,6 +159,7 @@ final class RequestHandler extends SingletonFactory
                     new EnforceCacheControlPrivate(),
                     new EnforceNoCacheForTemporaryRedirects(),
                     new EnforceFrameOptions(),
+                    new VaryAcceptLanguage(),
                 ]);
 
                 $response = $pipeline->process($psrRequest, $builtRequest);