From 7df9e706e821dd463cd7a7acdc1684fb7b2e0c40 Mon Sep 17 00:00:00 2001 From: tuutti Date: Tue, 8 Nov 2022 11:52:36 +0200 Subject: [PATCH 1/3] UHF-7126: Simplify proxy --- helfi_proxy.module | 85 +----- helfi_proxy.services.yml | 8 +- src/HttpMiddleware/AssetHttpMiddleware.php | 146 ---------- src/ProxyManager.php | 265 ++---------------- src/ProxyManagerInterface.php | 41 +-- src/Selector/AbsoluteUriAttributeSelector.php | 11 - src/Selector/AttributeSelector.php | 27 -- src/Selector/MultiValueAttributeSelector.php | 30 -- src/Selector/Selector.php | 21 -- src/Selector/SelectorInterface.php | 11 - src/Selector/SelectorRepositoryTrait.php | 34 --- src/Selector/StringValue.php | 11 - tests/src/Functional/AssetMidlewareTest.php | 1 - 13 files changed, 45 insertions(+), 646 deletions(-) delete mode 100644 src/HttpMiddleware/AssetHttpMiddleware.php delete mode 100644 src/Selector/AbsoluteUriAttributeSelector.php delete mode 100644 src/Selector/AttributeSelector.php delete mode 100644 src/Selector/MultiValueAttributeSelector.php delete mode 100644 src/Selector/Selector.php delete mode 100644 src/Selector/SelectorInterface.php delete mode 100644 src/Selector/SelectorRepositoryTrait.php delete mode 100644 src/Selector/StringValue.php diff --git a/helfi_proxy.module b/helfi_proxy.module index e03c3c2..0c9dc3e 100644 --- a/helfi_proxy.module +++ b/helfi_proxy.module @@ -7,49 +7,14 @@ use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\editor\Entity\Editor; -use Drupal\helfi_proxy\ProxyManagerInterface; /** - * Converts uri to absolute. - * - * @param string $uri - * The uri to convert. - * - * @return string - * The absolute url. + * Implements hook_file_url_alter(). */ -function helfi_proxy_absolute_url(string $uri) : string { - static $relative_path = ''; - - if (!$relative_path) { - /** @var \Drupal\helfi_proxy\ProxyManagerInterface $service */ - $service = \Drupal::service('helfi_proxy.proxy_manager'); - $relative_path = $service - ->getConfig(ProxyManagerInterface::ASSET_PATH); - } - /** @var \Drupal\Core\File\FileUrlGenerator $fileGenerator */ - $fileGenerator = \Drupal::service('file_url_generator'); - return '/' . $relative_path . $fileGenerator->transformRelative( - $fileGenerator->generateAbsoluteString($uri) - ); -} - -/** - * Checks if proxy is configured. - * - * @return bool - * TRUE if proxy should be used. - */ -function helfi_proxy_is_configured() : bool { - static $isConfigured = NULL; - - if ($isConfigured === NULL) { - /** @var \Drupal\helfi_proxy\ProxyManagerInterface $service */ - $service = \Drupal::service('helfi_proxy.proxy_manager'); - $isConfigured = $service->isConfigured(ProxyManagerInterface::ASSET_PATH); - } - return $isConfigured; +function helfi_proxy_file_url_alter(&$uri) : void { + /** @var \Drupal\helfi_proxy\ProxyManagerInterface $service */ + $service = \Drupal::service('helfi_proxy.proxy_manager'); + $uri = $service->processPath($uri); } /** @@ -59,24 +24,6 @@ function helfi_proxy_js_settings_alter( array &$settings, AttachedAssetsInterface $assets ) { - if (!helfi_proxy_is_configured()) { - return; - } - foreach (['designSelect', 'paragraphSelect'] as $type) { - if (!isset($settings[$type]['images'])) { - continue; - } - foreach ($settings[$type]['images'] as $field => $image) { - $settings[$type]['images'][$field] = helfi_proxy_absolute_url($image); - } - } - - foreach (['hdbtSubthemeIconsPath', 'iconsPath'] as $key) { - if (!isset($settings[$key])) { - continue; - } - $settings[$key] = helfi_proxy_absolute_url($settings[$key]); - } if (isset($settings['radioactivity'])) { /** @var \Drupal\helfi_proxy\ActiveSitePrefix $service */ $service = \Drupal::service('helfi_proxy.active_prefix'); @@ -90,28 +37,6 @@ function helfi_proxy_js_settings_alter( } } -/** - * Implements hook_editor_js_settings_alter(). - */ -function helfi_proxy_editor_js_settings_alter(array &$settings) { - if (!\Drupal::moduleHandler()->moduleExists('ckeditor') || !helfi_proxy_is_configured()) { - return; - } - $ckeditor_plugin_manager = \Drupal::service('plugin.manager.ckeditor.plugin'); - - foreach ($settings['editor']['formats'] as $format => &$format_settings) { - if ($format_settings['editor'] === 'ckeditor') { - $editor = Editor::load($format); - // @see \Drupal\ckeditor\Plugin\Editor\CKEditor::getJSSettings() - $external_plugin_files = $ckeditor_plugin_manager->getEnabledPluginFiles($editor); - // Convert editor css and plugin js to use absolute asset path. - $format_settings['editorSettings']['drupalExternalPlugins'] = array_map('helfi_proxy_absolute_url', $external_plugin_files); - $format_settings['editorSettings']['contentsCss'] = array_map('helfi_proxy_absolute_url', $format_settings['editorSettings']['contentsCss'] ?? []); - } - } - -} - /** * Implements hook_page_attachments_alter(). */ diff --git a/helfi_proxy.services.yml b/helfi_proxy.services.yml index 29722f3..ee75a1a 100644 --- a/helfi_proxy.services.yml +++ b/helfi_proxy.services.yml @@ -7,12 +7,6 @@ services: class: Drupal\helfi_proxy\ActiveSitePrefix arguments: ['@language_manager', '@config.factory'] - helfi_proxy.http_middleware: - class: Drupal\helfi_proxy\HttpMiddleware\AssetHttpMiddleware - arguments: ['@helfi_proxy.proxy_manager'] - tags: - - { name: http_middleware, priority: 500 } - helfi_proxy.session_configuration: class: Drupal\helfi_proxy\SessionConfiguration decorates: session_configuration @@ -41,7 +35,7 @@ services: helfi_proxy.proxy_manager: class: Drupal\helfi_proxy\ProxyManager - arguments: ['@config.factory'] + arguments: ['@config.factory', '@stream_wrapper_manager'] helfi_proxy.robots_response_subscriber: class: Drupal\helfi_proxy\EventSubscriber\RobotsResponseSubscriber diff --git a/src/HttpMiddleware/AssetHttpMiddleware.php b/src/HttpMiddleware/AssetHttpMiddleware.php deleted file mode 100644 index 0f62ea3..0000000 --- a/src/HttpMiddleware/AssetHttpMiddleware.php +++ /dev/null @@ -1,146 +0,0 @@ - $value) { - if (!isset($value['data']) || !is_string($value['data'])) { - continue; - } - $hasChanges = TRUE; - - $content[$key]['data'] = $this->proxyManager - ->processHtml($value['data'], $request); - } - return $hasChanges ? json_encode($content) : NULL; - } - - /** - * Checks if the given response is json. - * - * @param \Symfony\Component\HttpFoundation\Response $response - * The response. - * - * @return bool - * TRUE if response is JSON. - */ - private function isJsonResponse(Response $response) : bool { - if ($response instanceof JsonResponse) { - return TRUE; - } - - $jsonTypes = [ - 'application/json', - 'application/vnd.api+json', - ]; - return in_array($response->headers->get('content-type'), $jsonTypes); - } - - /** - * Checks for xml type mainly for sitemap. - * - * @param \Symfony\Component\HttpFoundation\Response $response - * The response. - * - * @return bool - * TRUE if response is XML - */ - private function isXmlResponse(Response $response) : bool { - if (!$response->headers->has('content-type')) { - return FALSE; - } - foreach (['application/xml', 'application/rss+xml'] as $type) { - if (str_starts_with($response->headers->get('content-type'), $type)) { - return TRUE; - } - } - return FALSE; - } - - /** - * {@inheritdoc} - */ - public function handle( - Request $request, - $type = self::MASTER_REQUEST, - $catch = TRUE - ) : Response { - $response = $this->httpKernel->handle($request, $type, $catch); - - if ($this->isXmlResponse($response)) { - return $response; - } - - // Nothing to do if asset path is not configured. - if (!$this->proxyManager->isConfigured(ProxyManagerInterface::ASSET_PATH)) { - return $response; - } - - $content = $response->getContent(); - - if (!is_string($content)) { - return $response; - } - - if ($this->isJsonResponse($response)) { - if ($json = $this->processJson($content, $request)) { - return $response->setContent($json); - } - return $response; - } - - $content = $this->proxyManager - ->processHtml($content, $request); - - return $response->setContent($content); - } - -} diff --git a/src/ProxyManager.php b/src/ProxyManager.php index 7a1c42a..6a584d9 100644 --- a/src/ProxyManager.php +++ b/src/ProxyManager.php @@ -5,28 +5,26 @@ namespace Drupal\helfi_proxy; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\helfi_proxy\Selector\AbsoluteUriAttributeSelector; -use Drupal\helfi_proxy\Selector\MultiValueAttributeSelector; -use Drupal\helfi_proxy\Selector\SelectorInterface; -use Drupal\helfi_proxy\Selector\SelectorRepositoryTrait; -use Drupal\helfi_proxy\Selector\StringValue; -use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\StreamWrapper\LocalStream; +use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; /** * A class to determine sites hostname. */ final class ProxyManager implements ProxyManagerInterface { - use ProxyTrait; - use SelectorRepositoryTrait; - /** * Constructs a new instance. * * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory * The config factory. + * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $streamWrapperManager + * The stream wrapper manager. */ - public function __construct(private ConfigFactoryInterface $configFactory) { + public function __construct( + private ConfigFactoryInterface $configFactory, + private StreamWrapperManagerInterface $streamWrapperManager, + ) { } /** @@ -46,166 +44,6 @@ public function getConfig(string $key, mixed $defaultValue = NULL) : mixed { return $config ?? $defaultValue; } - /** - * {@inheritdoc} - */ - public function processHtml(string $html, Request $request = NULL, array $selectors = []) : string { - $dom = new \DOMDocument(); - $previousXmlErrorBehavior = libxml_use_internal_errors(TRUE); - $encoding = ''; - - @$dom->loadHTML( - $encoding . $html, - LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD - ); - $dom->encoding = 'UTF-8'; - $xpath = new \DOMXPath($dom); - - foreach ($selectors ?: $this->getDefaultSelectors() as $selector) { - foreach ($xpath->query($selector->xpath) as $row) { - $value = $this - ->getAttributeValue( - $selector, - $row->getAttribute($selector->attribute), - $request - ); - - if (!$value) { - continue; - } - $row->setAttribute($selector->attribute, $value); - } - } - $result = trim($dom->saveHTML()); - libxml_use_internal_errors($previousXmlErrorBehavior); - - // Remove the debug xml encoding. - return str_replace($encoding, '', $result); - } - - /** - * {@inheritdoc} - */ - public function processValue(string $value, Request $request = NULL, array $selectors = []) : string { - $value = trim($value); - - if (!$selectors) { - $selectors[] = new StringValue(); - } - foreach ($selectors as $selector) { - $value = $this->getAttributeValue($selector, $value, $request); - } - return $value; - } - - /** - * Gets the attribute value. - * - * @param \Drupal\helfi_proxy\Selector\SelectorInterface $selector - * The selector. - * @param string|null $value - * The value. - * @param null|\Symfony\Component\HttpFoundation\Request $request - * The request. - * - * @return string|null - * The attribute value. - */ - private function getAttributeValue(SelectorInterface $selector, ?string $value, Request $request = NULL) : ? string { - // Skip if value is being served from CDN already. - if (!$value || $this->isCdnAddress($value)) { - return $value; - } - - // Certain elements might be absolute URLs already (such as og:image:url). - // Make sure locally hosted files are always served from correct domain. - if ($selector instanceof AbsoluteUriAttributeSelector) { - if (!$request) { - throw new \LogicException('Missing required Request $request.'); - } - $parts = parse_url($value); - - if (empty($parts['path'])) { - return $value; - } - - // Skip non-local assets. - if (!$this->isLocalAsset($parts['path'])) { - return $value; - } - - $value = $parts['path'] . (isset($parts['query']) ? '?' . $parts['query'] : NULL); - return sprintf('%s%s', $request->getSchemeAndHttpHost(), $this->addAssetPath($value)); - } - - // Ignore absolute URLs. - if ($this->isAbsoluteUri($value)) { - return $value; - } - - if ($selector instanceof MultiValueAttributeSelector) { - return $this->handleMultiValue($value, $selector->multivalueSeparator, - function (string $value) : string { - if ($this->isAbsoluteUri($value)) { - return $value; - } - return $this->addAssetPath($value); - } - ); - } - - return $this->addAssetPath($value); - } - - /** - * Checks if URI is absolute. - * - * @param string $uri - * The uri. - * - * @return bool - * TRUE if given uri is absolute. - */ - private function isAbsoluteUri(string $uri) : bool { - return str_starts_with($uri, 'http') || str_starts_with($uri, '//'); - } - - /** - * Checks if given URL is hosted from a CDN. - * - * @param string|null $value - * The value. - * - * @return bool - * TRUE if given url is CDN. - */ - private function isCdnAddress(?string $value) : bool { - if (!$value || !$domain = parse_url($value, PHP_URL_HOST)) { - return FALSE; - } - - static $patterns; - - if (!is_array($patterns)) { - $patterns = []; - - if ($stageFileProxy = getenv('STAGE_FILE_PROXY_ORIGIN')) { - $patterns[] = $this->parseHostName($stageFileProxy); - } - - if ($blobStorageName = getenv('AZURE_BLOB_STORAGE_NAME')) { - $patterns[] = sprintf('%s.blob.core.windows.net', $blobStorageName); - } - } - - foreach ($patterns as $pattern) { - if (str_starts_with($domain, $pattern)) { - return TRUE; - } - } - return FALSE; - } - /** * Checks whether the asset is hosted locally. * @@ -216,7 +54,7 @@ private function isCdnAddress(?string $value) : bool { * TRUE if asset is local. */ private function isLocalAsset(string $value) : bool { - foreach (['/sites', '/core', '/themes'] as $path) { + foreach (['sites/', 'core/', 'themes/', 'modules/'] as $path) { if (str_starts_with($value, $path)) { return TRUE; } @@ -225,78 +63,31 @@ private function isLocalAsset(string $value) : bool { } /** - * Prefixes the given value with /{asset-path}. - * - * @param string $value - * The value. - * - * @return string|null - * The path. + * {@inheritdoc} */ - private function addAssetPath(string $value) : ? string { - // Serve element from same domain via relative asset URL. Like: - // /assets/sites/default/files/js/{sha256}.js. - return sprintf('/%s/%s', $this->getConfig(self::ASSET_PATH), ltrim($value, '/')); - } + public function processPath(string $value) : ? string { + $assetPath = $this->getConfig(self::ASSET_PATH); - /** - * Handles tags with 'multipleValues' option. - * - * @param string $value - * The value. - * @param string $separator - * The separator. - * @param callable $callback - * The callback to run value through. - * - * @return string|null - * The value. - */ - private function handleMultiValue(string $value, string $separator, callable $callback) : ? string { - $parts = []; - foreach (explode($separator, $value) as $item) { - $parts[] = $callback(trim($item)); + if (str_starts_with($value, $assetPath)) { + return $value; } - return implode($separator, $parts); - } + $wrapper = $this->streamWrapperManager->getViaScheme( + $this->streamWrapperManager::getScheme($value) + ); - /** - * Gets the asset path. - * - * phpcs:ignore - * @deprecated in helfi_proxy:2.0.3 and is removed from helfi_proxy:3.0.0. Use ::getConfig() instead. - * - * @return string|null - * The asset path. - */ - public function getAssetPath() : ? string { - return $this->getConfig(self::ASSET_PATH); - } + $path = $value; - /** - * Gets the configured prefixes. - * - * phpcs:ignore - * @deprecated in helfi_proxy:2.0.3 and is removed from helfi_proxy:3.0.0. Use ::getConfig() instead. - * - * @return array - * The instance prefixes. - */ - public function getInstancePrefixes() : array { - return $this->getConfig(self::PREFIXES, []); - } + // Convert public:// paths to relative. + if ($wrapper instanceof LocalStream) { + $path = $wrapper->getDirectoryPath() . '/' . $this->streamWrapperManager::getTarget($value); + } - /** - * Gets the tunnistamo return url. - * - * phpcs:ignore - * @deprecated in helfi_proxy:2.0.3 and is removed from helfi_proxy:3.0.0. Use ::getConfig() instead. - * - * @return string - * The return url. - */ - public function getTunnistamoReturnUrl() : ? string { - return $this->getConfig(self::TUNNISTAMO_RETURN_URL); + if (!$this->isLocalAsset($path)) { + return $value; + } + // Serve element from same domain via relative asset URL. Like: + // /assets/sites/default/files/js/{sha256}.js. + return sprintf('/%s/%s', $this->getConfig(self::ASSET_PATH), ltrim($path, '/')); } } diff --git a/src/ProxyManagerInterface.php b/src/ProxyManagerInterface.php index e04229c..9ca3fb4 100644 --- a/src/ProxyManagerInterface.php +++ b/src/ProxyManagerInterface.php @@ -20,36 +20,6 @@ interface ProxyManagerInterface { public const ROBOTS_HEADER_ENABLED = 'robots_header_enabled'; public const TUNNISTAMO_RETURN_URL = 'tunnistamo_return_url'; - /** - * Manipulates the given attributes to have correct values. - * - * @param string $html - * The html to manipulate. - * @param null|\Symfony\Component\HttpFoundation\Request $request - * The request. - * @param array $selectors - * The selectors. - * - * @return \Symfony\Component\HttpFoundation\Response - * The manipulated response. - */ - public function processHtml(string $html, Request $request = NULL, array $selectors = []) : string; - - /** - * Processes a string value. - * - * @param string $value - * The value. - * @param null|\Symfony\Component\HttpFoundation\Request $request - * The request. - * @param array $selectors - * The selectors. - * - * @return string - * The processed value. - */ - public function processValue(string $value, Request $request = NULL, array $selectors = []) : string; - /** * Whether the proxy is configured or not. * @@ -74,4 +44,15 @@ public function isConfigured(string $key) : bool; */ public function getConfig(string $key, mixed $defaultValue = NULL) : mixed; + /** + * Prefixes the given value with /{asset-path}. + * + * @param string $value + * The value. + * + * @return string|null + * The path. + */ + public function processPath(string $value) : ? string; + } diff --git a/src/Selector/AbsoluteUriAttributeSelector.php b/src/Selector/AbsoluteUriAttributeSelector.php deleted file mode 100644 index 2316272..0000000 --- a/src/Selector/AbsoluteUriAttributeSelector.php +++ /dev/null @@ -1,11 +0,0 @@ - Date: Tue, 8 Nov 2022 15:06:34 +0200 Subject: [PATCH 2/3] UHF-7126: Test fixes --- src/ProxyManager.php | 14 +- src/ProxyManagerInterface.php | 2 - tests/src/Functional/AssetMidlewareTest.php | 251 ------------------- tests/src/Kernel/ProxyManagerTest.php | 254 +------------------- tests/src/Unit/AssetHttpMiddlewareTest.php | 109 --------- 5 files changed, 18 insertions(+), 612 deletions(-) delete mode 100644 tests/src/Functional/AssetMidlewareTest.php delete mode 100644 tests/src/Unit/AssetHttpMiddlewareTest.php diff --git a/src/ProxyManager.php b/src/ProxyManager.php index 6a584d9..6b77536 100644 --- a/src/ProxyManager.php +++ b/src/ProxyManager.php @@ -54,12 +54,7 @@ public function getConfig(string $key, mixed $defaultValue = NULL) : mixed { * TRUE if asset is local. */ private function isLocalAsset(string $value) : bool { - foreach (['sites/', 'core/', 'themes/', 'modules/'] as $path) { - if (str_starts_with($value, $path)) { - return TRUE; - } - } - return FALSE; + return (bool) preg_match('/^(sites|core|themes|modules)\/\w/', $value); } /** @@ -68,18 +63,21 @@ private function isLocalAsset(string $value) : bool { public function processPath(string $value) : ? string { $assetPath = $this->getConfig(self::ASSET_PATH); - if (str_starts_with($value, $assetPath)) { + if (!$assetPath || str_starts_with($value, $assetPath)) { return $value; } $wrapper = $this->streamWrapperManager->getViaScheme( $this->streamWrapperManager::getScheme($value) ); - $path = $value; + $path = ltrim($value, '/'); // Convert public:// paths to relative. if ($wrapper instanceof LocalStream) { $path = $wrapper->getDirectoryPath() . '/' . $this->streamWrapperManager::getTarget($value); + // KernelTests will convert public://file to vfs://root/simpletest/file. + // Remove vfs part to make testing possible. + $path = str_replace('vfs://root/', '', $path); } if (!$this->isLocalAsset($path)) { diff --git a/src/ProxyManagerInterface.php b/src/ProxyManagerInterface.php index 9ca3fb4..9cbacc7 100644 --- a/src/ProxyManagerInterface.php +++ b/src/ProxyManagerInterface.php @@ -4,8 +4,6 @@ namespace Drupal\helfi_proxy; -use Symfony\Component\HttpFoundation\Request; - /** * Proxy manager interface. */ diff --git a/tests/src/Functional/AssetMidlewareTest.php b/tests/src/Functional/AssetMidlewareTest.php deleted file mode 100644 index d6154ed..0000000 --- a/tests/src/Functional/AssetMidlewareTest.php +++ /dev/null @@ -1,251 +0,0 @@ -config('helfi_proxy.settings') - ->set('asset_path', 'test-assets') - ->set('prefixes', [ - 'sv' => 'prefix-sv', - 'en' => 'prefix-en', - 'fi' => 'prefix-fi', - ]) - ->save(); - - $full_html_format = FilterFormat::create([ - 'format' => 'full_html', - 'name' => 'Full HTML', - 'weight' => 1, - 'filters' => [], - ]); - $full_html_format->save(); - - $this->drupalCreateContentType(['type' => 'page']); - $this->node = $this->drupalCreateNode([ - 'body' => [ - 'value' => ' - - Helsinki - - - - ', - 'format' => 'full_html', - ], - ]); - } - - /** - * Asserts that asset urls are replaced properly. - * - * @param array $types - * A key value list of tag -> attribute values. - */ - private function assertAssetPaths(array $types) : void { - foreach ($types as $type) { - $counter = 0; - - $elements = $this->getSession()->getPage()->findAll('css', $type['tag']); - - foreach ($elements as $row) { - if (!$value = $row->getAttribute($type['attribute'])) { - continue; - } - $this->assertStringStartsWith($type['expected'], $value); - $counter++; - } - // Make sure we have at least one asset with replaced url. - $this->assertTrue($counter > 0); - } - } - - /** - * Tests css and js paths. - */ - public function testAssetPaths() : void { - // Make sure node canonical url works. - $this->drupalGet($this->node->toUrl()); - $this->assertAssetPaths([ - [ - 'tag' => 'img', - 'attribute' => 'src', - 'expected' => '/test-assets/themes/test.jpg', - ], - [ - 'tag' => 'link[rel="stylesheet"]', - 'attribute' => 'href', - 'expected' => '/test-assets/', - ], - [ - 'tag' => 'script', - 'attribute' => 'src', - 'expected' => '/test-assets/', - ], - ]); - - // Make sure post requests work when we have form errors. - $this->drupalGet(Url::fromRoute('user.login')); - $this->submitForm([ - 'name' => 'helfi-admin', - 'pass' => '111', - ], 'Log in'); - $this->assertAssetPaths([ - [ - 'tag' => 'link[rel="stylesheet"]', - 'attribute' => 'href', - 'expected' => '/test-assets/', - ], - [ - 'tag' => 'script', - 'attribute' => 'src', - 'expected' => '/test-assets/', - ], - ]); - - // Test node edit form. - $this->drupalLogin($this->rootUser); - $this->drupalGet($this->node->toUrl('edit-form')); - $this->assertAssetPaths([ - [ - 'tag' => 'link[rel="stylesheet"]', - 'attribute' => 'href', - 'expected' => '/test-assets/', - ], - [ - 'tag' => 'script', - 'attribute' => 'src', - 'expected' => '/test-assets', - ], - ]); - - $path = $this->getSession() - ->getPage() - ->find('css', '.form-autocomplete') - ->getAttribute('data-autocomplete-path'); - - $this->submitForm([], 'Save'); - $this->assertAssetPaths([ - [ - 'tag' => 'img', - 'attribute' => 'src', - 'expected' => '/test-assets/themes/test.jpg', - ], - [ - 'tag' => 'link[rel="stylesheet"]', - 'attribute' => 'href', - 'expected' => '/test-assets/', - ], - [ - 'tag' => 'script', - 'attribute' => 'src', - 'expected' => '/test-assets', - ], - ]); - - // Test json response (autocomplete field). - $this->drupalGet($path, ['query' => ['q' => 'Anonymous']]); - $json = json_decode($this->getSession()->getPage()->getContent()); - $this->assertEquals('Anonymous', $json[0]->label); - } - - /** - * Tests meta tags. - */ - public function testMetaTags() : void { - $html = trim(' - - - - - - - - - - - - - - - - '); - - $request = Request::createFromGlobals(); - $kernelMock = $this->createMock(HttpKernelInterface::class); - $kernelMock->method('handle') - ->willReturn(new Response($html)); - $sut = new AssetHttpMiddleware( - $kernelMock, - $this->container->get('helfi_proxy.proxy_manager') - ); - - $expected = new FormattableMarkup( - ' - - - - - - - - - - - - - - - - ', ['@hostname' => $this->getHostname()]); - - $response = $sut->handle($request)->getContent(); - $this->assertEquals(trim((string) $expected), trim($response)); - } - -} diff --git a/tests/src/Kernel/ProxyManagerTest.php b/tests/src/Kernel/ProxyManagerTest.php index 6f7e5bd..612bd5e 100644 --- a/tests/src/Kernel/ProxyManagerTest.php +++ b/tests/src/Kernel/ProxyManagerTest.php @@ -6,12 +6,7 @@ use Drupal\helfi_proxy\ProxyManager; use Drupal\helfi_proxy\ProxyManagerInterface; -use Drupal\helfi_proxy\ProxyTrait; -use Drupal\helfi_proxy\Selector\AbsoluteUriAttributeSelector; -use Drupal\helfi_proxy\Selector\AttributeSelector; -use Drupal\helfi_proxy\Selector\MultiValueAttributeSelector; use Drupal\KernelTests\KernelTestBase; -use Symfony\Component\HttpFoundation\Request; /** * Tests Proxy manager. @@ -21,8 +16,6 @@ */ class ProxyManagerTest extends KernelTestBase { - use ProxyTrait; - /** * {@inheritdoc} */ @@ -42,25 +35,6 @@ private function proxyManager() : ProxyManager { return $this->container->get('helfi_proxy.proxy_manager'); } - /** - * Creates a mock request. - * - * @param string $host - * The host. - * @param int $port - * The port. - * @param string $uri - * The uri. - * - * @return \Symfony\Component\HttpFoundation\Request - * The request. - */ - private function createRequest(string $host = '127.0.0.1', int $port = 8888, string $uri = '') : Request { - return Request::create($uri, server: [ - 'HTTP_HOST' => $host . ':' . $port, - ]); - } - /** * Tests instance prefixes. * @@ -151,227 +125,23 @@ public function testAssetPath() : void { } /** - * Constructs a HTML tag. + * Tests process path. * - * @param string $tag - * The tag. - * @param array $attributes - * The attributes. - * @param string|null $value - * The value or null. - * @param bool $hasClosingTag - * Whether we should include closing tag or not. - * - * @return string - * The created html tag. - */ - private function createHtmlTag( - string $tag, - array $attributes, - string $value = NULL, - bool $hasClosingTag = TRUE - ) : string { - return vsprintf('<%s %s>%s%s', [ - $tag, - implode(' ', array_map(function ($key, $value) { - return sprintf('%s="%s"', $key, $value); - }, array_keys($attributes), $attributes)), - $value, - $hasClosingTag ? "" : NULL, - ]); - } - - /** - * Tests script tag attribute value. - * - * @covers ::processHtml - * @covers ::getAttributeValue - * @covers ::getDefaultSelectors - * @covers ::isAbsoluteUri - * @covers ::addAssetPath - * @covers ::__construct - */ - public function testAttributeSelector() : void { - $this->setAssetPath('test-assets'); - $request = $this->createRequest(); - - $expected = $this->createHtmlTag('script', ['src' => '/test-assets/core/modules/system/test.js']); - $html = $this->createHtmlTag('script', ['src' => '/core/modules/system/test.js']); - - $processed = $this->proxyManager() - ->processHtml($html, $request, [new AttributeSelector('//script', 'src')]); - $this->assertEquals($expected, $processed); - } - - /** - * Tests string value processing. - * - * @covers ::processValue - * @covers ::getAttributeValue - * @covers ::getDefaultSelectors - * @covers ::isAbsoluteUri - * @covers ::addAssetPath - * @covers ::__construct - */ - public function testStringValueSelector() : void { - $this->setAssetPath('test-assets'); - - $processed = $this->proxyManager()->processValue('/core/modules/system/test.js'); - $this->assertEquals('/test-assets/core/modules/system/test.js', $processed); - } - - /** - * Tests AbsoluteUriAttributeSelector() object. - * - * @covers ::processHtml - * @covers ::getAttributeValue - * @covers ::getDefaultSelectors - * @covers ::isAbsoluteUri - * @covers ::addAssetPath * @covers ::isLocalAsset - * @covers ::isCdnAddress - * @covers ::__construct - */ - public function testAbsoluteUriAttributeSelector() : void { - $request = $this->createRequest(); - $this->setAssetPath('test-assets'); - $xpaths = [ - 'og:image:url' => 'property', - 'twitter:image' => 'name', - ]; - - foreach ($xpaths as $value => $selector) { - // Make sure domain is added to relative paths when dealing with - // AbsoluteUriAttributeSelectors. - $expected = $this->createHtmlTag('meta', [ - $selector => $value, - 'content' => 'http://' . $this->getHostname() . '/test-assets/themes/contrib/hdbt/test.png', - ], hasClosingTag: FALSE); - $html = $this->createHtmlTag('meta', [ - $selector => $value, - 'content' => '/themes/contrib/hdbt/test.png', - ], hasClosingTag: FALSE); - - $xpath = sprintf('//meta[@%s="%s"]', $selector, $value); - $attributeSelector = new AbsoluteUriAttributeSelector($xpath, 'content'); - - $this->assertEquals( - $expected, - $this->proxyManager()->processHtml($html, $request, [$attributeSelector]) - ); - - // Make sure the domain is converted to correct one. - $html = $this->createHtmlTag('meta', [ - $selector => $value, - 'content' => 'http://www.hel.fi/themes/contrib/hdbt/test.png', - ], hasClosingTag: FALSE); - - $this->assertEquals( - $expected, - $this->proxyManager()->processHtml($html, $request, [$attributeSelector]) - ); - } - } - - /** - * Tests blob storage url when blob storage name is set. - * - * @covers ::processHtml - * @covers ::getAttributeValue - * @covers ::getDefaultSelectors - * @covers ::isCdnAddress - * @covers ::addAssetPath - * @covers ::isLocalAsset - * @covers ::__construct - */ - public function testBlobStorageUrlWithStorage() : void { - putenv('STAGE_FILE_PROXY_ORIGIN='); - putenv('AZURE_BLOB_STORAGE_NAME=kymp'); - $request = $this->createRequest(); - - $html = $this->createHtmlTag('meta', [ - 'property' => 'og:image:url', - 'content' => 'https://kymp.blob.core.windows.net/test/og-image.png?itok=123', - ], hasClosingTag: FALSE); - // Make sure file is served from blob storage when blob storage container - // is set. - $this->assertEquals( - $html, - $this->proxyManager()->processHtml($html, $request, [ - new AbsoluteUriAttributeSelector('//meta[@property="og:image:url"]', 'content'), - ]) - ); - } - - /** - * Tests blob storage url when STAGE_FILE_PROXY_ORIGIN is set. - * - * @covers ::processHtml - * @covers ::getAttributeValue - * @covers ::getDefaultSelectors - * @covers ::isCdnAddress - * @covers ::addAssetPath - * @covers ::isLocalAsset - * @covers ::__construct + * @covers ::getConfig + * @covers ::processPath */ - public function testBlobStorageUrlWithStageFileProxy() : void { - putenv('STAGE_FILE_PROXY_ORIGIN=https://kymp.blob.core.windows.net'); - putenv('AZURE_BLOB_STORAGE_NAME=sote'); - $request = $this->createRequest(); - // Make sure file is served from blob storage when blob storage container - // is set. - $html = $this->createHtmlTag('meta', [ - 'property' => 'og:image:url', - 'content' => 'https://sote.blob.core.windows.net/test/og-image.png', - ], hasClosingTag: FALSE); - - $this->assertEquals( - $html, - $this->proxyManager()->processHtml($html, $request, [ - new AbsoluteUriAttributeSelector('//meta[@property="og:image:url"]', 'content'), - ]) - ); - } + public function testProcessPath() : void { + // Make sure nothing is done if asset path is not configured. + $this->assertSame('/sites/default/files/test.jpg', $this->proxyManager()->processPath('/sites/default/files/test.jpg')); - /** - * Tests source srcset. - * - * @covers ::processHtml - * @covers ::getAttributeValue - * @covers ::getDefaultSelectors - * @covers ::isCdnAddress - * @covers ::addAssetPath - * @covers ::isAbsoluteUri - * @covers ::handleMultiValue - * @covers ::__construct - */ - public function testMultivalueAttributeSelector() : void { - $request = $this->createRequest(); $this->setAssetPath('test-assets'); - - $values = [ - '/sites/default/files/styles/test/public/image.png?h=948e8679&itok=FwETi0jH 1x', - '/sites/default/files/styles/test/public/image.png?h=948e8679&itok=FwETi0jH 1x', - '//helfi-kymp.docker.so/sites/default/files/styles/3_2_xxs_2x/public/image%20%281%29.png?itok=pSa7Ws3i 2x', - ]; - $html = $this->createHtmlTag('source', ['srcset' => implode(', ', $values)]); - - $expectedValues = [ - '/test-assets/sites/default/files/styles/test/public/image.png?h=948e8679&itok=FwETi0jH 1x', - '/test-assets/sites/default/files/styles/test/public/image.png?h=948e8679&itok=FwETi0jH 1x', - // Make sure absolute uris are ignored. - '//helfi-kymp.docker.so/sites/default/files/styles/3_2_xxs_2x/public/image%20%281%29.png?itok=pSa7Ws3i 2x', - ]; - $expected = $this->createHtmlTag('source', [ - 'srcset' => implode(', ', $expectedValues), - ]); - - $this->assertEquals( - $expected, - $this->proxyManager()->processHtml($html, $request, [ - new MultiValueAttributeSelector('//source', 'srcset', ', '), - ]) - ); + $this->assertSame('/test-assets/sites/default/files/test.jpg', $this->proxyManager()->processPath('/sites/default/files/test.jpg')); + $actual = $this->proxyManager()->processPath('public://test.jpg'); + // KernelTests store files in sites/simpletest/{id}/ folder. Test + // start and end only. + $this->assertStringStartsWith('/test-assets/sites/simpletest', $actual); + $this->assertStringEndsWith('files/test.jpg', $actual); } } diff --git a/tests/src/Unit/AssetHttpMiddlewareTest.php b/tests/src/Unit/AssetHttpMiddlewareTest.php deleted file mode 100644 index 5d39593..0000000 --- a/tests/src/Unit/AssetHttpMiddlewareTest.php +++ /dev/null @@ -1,109 +0,0 @@ -prophesize(ParameterBag::class); - $headers->has('content-type') - ->shouldBeCalled() - ->willReturn(TRUE); - - $headers->set('content-type', $contentType); - $headers->get('content-type') - ->shouldBeCalled() - ->willReturn($contentType); - - $responseMock = new Response(); - $responseMock->setContent('123'); - $responseMock->headers = $headers->reveal(); - - $mockHttpKernel = $this->createMock(HttpKernelInterface::class); - $mockHttpKernel->method('handle') - ->willReturn($responseMock); - - $proxyManagerMock = $this->prophesize(ProxyManagerInterface::class); - $proxyManagerMock->isConfigured(ProxyManagerInterface::ASSET_PATH) - ->shouldNotBeCalled(); - - $requestMock = $this->createMock(Request::class); - $sut = new AssetHttpMiddleware($mockHttpKernel, $proxyManagerMock->reveal()); - $this->assertEquals( - $sut->handle($requestMock)->headers->get('content-type'), - $contentType - ); - } - - /** - * The data provider for xml response test. - * - * @return \string[][] - * The data. - */ - public function xmlResponseProvider() : array { - return [ - ['application/xml'], - ['application/xml; charset=utf-8'], - ['application/rss+xml; charset=utf-8'], - ]; - } - - /** - * Tests that response is intact when no asset path is configured. - * - * @covers ::handle - * @covers ::__construct - * @covers ::isXmlResponse - */ - public function testNoAssetPathConfigured() : void { - $headers = $this->prophesize(ParameterBag::class); - $headers->has('content-type') - ->shouldBeCalled() - ->willReturn(FALSE); - $responseMock = new Response(); - $responseMock->setContent('123'); - $responseMock->headers = $headers->reveal(); - - $mockHttpKernel = $this->createMock(HttpKernelInterface::class); - $mockHttpKernel->method('handle') - ->willReturn($responseMock); - - $proxyManagerMock = $this->prophesize(ProxyManagerInterface::class); - $proxyManagerMock->isConfigured(ProxyManagerInterface::ASSET_PATH) - ->shouldBeCalled() - ->willReturn(FALSE); - - $sut = new AssetHttpMiddleware($mockHttpKernel, $proxyManagerMock->reveal()); - $requestMock = $this->createMock(Request::class); - $sut->handle($requestMock); - } - -} From aeb9f41af5b8eec55cbd9efceefb3eff55a54976 Mon Sep 17 00:00:00 2001 From: tuutti Date: Tue, 8 Nov 2022 15:25:07 +0200 Subject: [PATCH 3/3] UHF-7126: Use asset path variable --- src/ProxyManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProxyManager.php b/src/ProxyManager.php index 6b77536..2f1711a 100644 --- a/src/ProxyManager.php +++ b/src/ProxyManager.php @@ -85,7 +85,7 @@ public function processPath(string $value) : ? string { } // Serve element from same domain via relative asset URL. Like: // /assets/sites/default/files/js/{sha256}.js. - return sprintf('/%s/%s', $this->getConfig(self::ASSET_PATH), ltrim($path, '/')); + return sprintf('/%s/%s', $assetPath, ltrim($path, '/')); } }