Skip to content

Commit

Permalink
fix: avoid manipulate absolute stream resource URL in web-component m…
Browse files Browse the repository at this point in the history
…ode (#13296) (CP: 2.7) (#13306)

* fix: avoid manipulate absolute stream resource URL in web-component mode  (#13296)

In web-component mode resource URLs are always prefixed by serviceUrl,
but sometimes the URL is already absolute (e.g. in Liferay).
With this change absolute URLs are used as-is also in web-component mode.

Fixes #13288

* test: fixed StreamResource test in compatibility mode

Co-authored-by: Marco Collovati <marco@vaadin.com>
  • Loading branch information
vaadin-bot and mcollovati committed Mar 16, 2022
1 parent ccc3dfa commit c4781aa
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 1 deletion.
16 changes: 16 additions & 0 deletions flow-client/src/main/java/com/vaadin/client/WidgetUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ public static String getAbsoluteUrl(String url) {
return a.getHref();
}

/**
* Detects if an URL is absolute.
*
* URLs wihtout schema but starting with double slashes (e.g. //myhost/path}
* are considered absolute.
*
* @param url
* a string with the URL to check
* @return {@literal true} if the url is absolute, otherwise
* {@literal false}.
*/
public static native boolean isAbsoluteUrl(String url)
/*-{
return !!url.match(/^(?:[a-zA-Z]+:)?\/\//);
}-*/;

/**
* Anything in, anything out. It's JavaScript after all. This method just
* makes the Java compiler accept the fact.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1481,7 +1481,8 @@ private static void updateAttributeValue(
+ attribute + "' but it has no "
+ NodeProperties.URI_ATTRIBUTE + " key";
String uri = object.getString(NodeProperties.URI_ATTRIBUTE);
if (configuration.isWebComponentMode()) {
if (configuration.isWebComponentMode()
&& !WidgetUtil.isAbsoluteUrl(uri)) {
String baseUri = configuration.getServiceUrl();
baseUri = baseUri.endsWith("/") ? baseUri : baseUri + "/";
WidgetUtil.updateAttribute(element, attribute,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.webcomponent;

import java.nio.charset.StandardCharsets;

import com.vaadin.flow.component.WebComponentExporter;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.webcomponent.WebComponent;
import com.vaadin.flow.server.StreamResource;

public class StreamResourceExporter extends WebComponentExporter<Div> {

public StreamResourceExporter() {
super("vaadin-stream-resource");
}

@Override
protected void configureInstance(WebComponent<Div> webComponent,
Div component) {
StreamResource relativeUrl = createStreamResource("relativeURL");
Anchor relativeLink = new Anchor(relativeUrl, "Relative Link");
relativeLink.setId("relativeLink");

StreamResource absoluteUrl = createStreamResource("absoluteURL");
Anchor absoluteLink = new Anchor(absoluteUrl, "Absolute Link");
absoluteLink.setId("absoluteLink");

StreamResource schemalessUrl = createStreamResource(
"absoluteURLschemaless");
Anchor schemalessLink = new Anchor(schemalessUrl, "Schemaless Link");
schemalessLink.setId("schemalessLink");

component.add(relativeLink, absoluteLink, schemalessLink);
}

private StreamResource createStreamResource(String type) {
return new StreamResource(type + ".txt", (stream, session) -> stream
.write(type.getBytes(StandardCharsets.UTF_8)));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.webcomponent.servlets;

import javax.servlet.annotation.WebServlet;

import java.net.URI;

import com.vaadin.flow.function.DeploymentConfiguration;
import com.vaadin.flow.server.AbstractStreamResource;
import com.vaadin.flow.server.ServiceException;
import com.vaadin.flow.server.StreamResourceRegistry;
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.VaadinServletService;
import com.vaadin.flow.server.VaadinSession;

@WebServlet(urlPatterns = { "/vaadin-stream-resources/*",
"/vaadin-stream-resources-absolute/*" }, asyncSupported = true)
public class CustomStreamResourceRegistryServlet extends VaadinServlet {

@Override
protected VaadinServletService createServletService(
DeploymentConfiguration deploymentConfiguration)
throws ServiceException {
VaadinServletService service = new VaadinServletService(this,
deploymentConfiguration) {

@Override
protected VaadinSession createVaadinSession(VaadinRequest request) {
return new VaadinSession(this) {
@Override
protected StreamResourceRegistry createStreamResourceRegistry() {
return new StreamResourceRegistry(this) {
@Override
public URI getTargetURI(
AbstractStreamResource resource) {
URI targetURI = super.getTargetURI(resource);
if (resource.getName()
.contains("absoluteURL")) {
String baseURL = getCurrentServletRequest()
.getRequestURL().toString()
.replaceFirst(
"(/vaadin-stream-resources)/.*",
"$1-absolute/");
if (resource.getName()
.contains("schemaless")) {
baseURL = baseURL.replaceFirst(
"^https?://", "//");
}
return URI.create(baseURL)
.resolve(targetURI);
}
return targetURI;
}
};
}
};
}
};
service.init();
return service;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.webcomponent;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;

import com.vaadin.flow.testutil.ChromeBrowserTest;
import com.vaadin.testbench.TestBenchElement;

public class StreamResourceIT extends ChromeBrowserTest {

@Override
protected String getTestPath() {
return Constants.PAGE_CONTEXT + "/streamResource.html";
}

@Test
public void absoluteUrlsAreNotModified() {
open();

waitForElementVisible(By.tagName("vaadin-stream-resource"));

TestBenchElement downloadLink = $("vaadin-stream-resource").first()
.$("a").id("absoluteLink");

String url = downloadLink.getAttribute("href");
Assert.assertTrue(
"Url is not generated by custom StreamResourceRegistry",
url.contains("/vaadin-stream-resources-absolute/"));
Assert.assertEquals(
"Absolute url has been prefixed with other content: " + url, 0,
url.lastIndexOf("http://"));
}

@Test
public void schemaLessUrlsAreNotModified() {
open();

waitForElementVisible(By.tagName("vaadin-stream-resource"));

TestBenchElement downloadLink = $("vaadin-stream-resource").first()
.$("a").id("schemalessLink");

String url = downloadLink.getAttribute("href");
Assert.assertTrue(
"Url is not generated by custom StreamResourceRegistry",
url.contains("/vaadin-stream-resources-absolute/"));
Assert.assertEquals(
"Absolute url has been prefixed with other content: " + url, 0,
url.lastIndexOf("http://"));
}

@Test
public void relativeUrlsArePrefixedWithServiceUrl() {
open();

waitForElementVisible(By.tagName("vaadin-stream-resource"));

TestBenchElement downloadLink = $("vaadin-stream-resource").first()
.$("a").id("relativeLink");

String url = downloadLink.getAttribute("href");
Assert.assertTrue(
"Url is not generated by custom StreamResourceRegistry",
url.contains("/vaadin-stream-resources/"));
Assert.assertEquals(
"Absolute url has been prefixed with other content: " + url, 0,
url.lastIndexOf("http://"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!--
~ Copyright 2000-2022 Vaadin Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
~ use this file except in compliance with the License. You may obtain a copy of
~ the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
~ License for the specific language governing permissions and limitations under
~ the License.
-->

<!doctype html>

<head>
<script type="text/javascript" src="/frontend/bower_components/webcomponentsjs/webcomponents-loader.js"></script>
<link rel="import" href="/vaadin-stream-resources/web-component/vaadin-stream-resource.html">
</head>

<body>
<p>
StreamResource in Web components
</p>

<vaadin-stream-resource></vaadin-stream-resource>

</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!--
~ Copyright 2000-2022 Vaadin Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
~ use this file except in compliance with the License. You may obtain a copy of
~ the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
~ License for the specific language governing permissions and limitations under
~ the License.
-->

<!doctype html>

<head>
<script type="text/javascript"
src="/vaadin-stream-resources/web-component/vaadin-stream-resource.html"></script>
</head>

<body>
<p>
StreamResource in Web components
</p>

<vaadin-stream-resource></vaadin-stream-resource>

</body>
31 changes: 31 additions & 0 deletions flow-tests/test-embedding/webapp/npm/streamResource.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!--
~ Copyright 2000-2022 Vaadin Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
~ use this file except in compliance with the License. You may obtain a copy of
~ the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
~ License for the specific language governing permissions and limitations under
~ the License.
-->

<!doctype html>

<head>
<script type='module'
src='/vaadin-stream-resources/web-component/vaadin-stream-resource.js'></script>
</head>

<body>
<p>
StreamResource in Web components
</p>

<vaadin-stream-resource></vaadin-stream-resource>

</body>

0 comments on commit c4781aa

Please sign in to comment.