From 8ef7422c3c89080766e4ae41076b01d8e1eb902c Mon Sep 17 00:00:00 2001 From: Vasili Gulevich Date: Sat, 21 Oct 2023 16:43:26 +0400 Subject: [PATCH] Follow P2 contract of cached file's extension (#2938) P2 relies on correct file extensions to parse cached files. Fixes #2938 See https://github.com/eclipse-equinox/p2/issues/355 --- .../transport/SharedHttpCacheStorage.java | 10 +- .../projects/target.content_jar/category.xml | 4 + tycho-its/projects/target.content_jar/pom.xml | 49 +++++++ .../target.content_jar/targetplatform.target | 11 ++ .../repositories/content_jar/artifacts.jar | Bin 0 -> 580 bytes .../repositories/content_jar/content.jar | Bin 0 -> 988 bytes ...sue_2938_reproducer_1.0.0.202310211419.jar | Bin 0 -> 356 bytes .../tycho/test/tycho2938/ContentJarTest.java | 120 ++++++++++++++++++ .../eclipse/tycho/test/util/HttpServer.java | 33 +++++ 9 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 tycho-its/projects/target.content_jar/category.xml create mode 100644 tycho-its/projects/target.content_jar/pom.xml create mode 100644 tycho-its/projects/target.content_jar/targetplatform.target create mode 100644 tycho-its/repositories/content_jar/artifacts.jar create mode 100644 tycho-its/repositories/content_jar/content.jar create mode 100644 tycho-its/repositories/content_jar/features/issue_2938_reproducer_1.0.0.202310211419.jar create mode 100644 tycho-its/src/test/java/org/eclipse/tycho/test/tycho2938/ContentJarTest.java diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java index ca3e8cc54d..32237fc0e8 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java @@ -262,8 +262,16 @@ public synchronized File fetchFile(URI uri, HttpTransportFactory transportFactor } updateHeader(response, code); if (isRedirected(code)) { - return SharedHttpCacheStorage.this.getCacheEntry(getRedirect(uri), logger) + File cachedFile = SharedHttpCacheStorage.this.getCacheEntry(getRedirect(uri), logger) .getCacheFile(transportFactory); + // https://github.com/eclipse-tycho/tycho/issues/2938 + // Redirect may change extension. P2's SimpleMetadataRepositoryFactory relies on + // accurate file extension to be cached. + // Copying file to accommodate original request and its file extension. + // Once https://github.com/eclipse-equinox/p2/issues/355 is fixed, cachedFile + // may be returned directly without copying. + FileUtils.copyFile(cachedFile, file); + return file; } if (exits) { FileUtils.forceDelete(file); diff --git a/tycho-its/projects/target.content_jar/category.xml b/tycho-its/projects/target.content_jar/category.xml new file mode 100644 index 0000000000..7eaddf9158 --- /dev/null +++ b/tycho-its/projects/target.content_jar/category.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tycho-its/projects/target.content_jar/pom.xml b/tycho-its/projects/target.content_jar/pom.xml new file mode 100644 index 0000000000..a90b318d2e --- /dev/null +++ b/tycho-its/projects/target.content_jar/pom.xml @@ -0,0 +1,49 @@ + + 4.0.0 + tycho-its-project.p2Repository.slicerDependencies + aggregator + 1.0.0 + eclipse-repository + + + UTF-8 + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + + org.eclipse.tycho + target-platform-configuration + ${tycho-version} + + p2 + JavaSE-11 + + targetplatform.target + + + + win32 + win32 + x86_64 + + + linux + gtk + x86_64 + + + + + + + + diff --git a/tycho-its/projects/target.content_jar/targetplatform.target b/tycho-its/projects/target.content_jar/targetplatform.target new file mode 100644 index 0000000000..1f4ffb4d15 --- /dev/null +++ b/tycho-its/projects/target.content_jar/targetplatform.target @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tycho-its/repositories/content_jar/artifacts.jar b/tycho-its/repositories/content_jar/artifacts.jar new file mode 100644 index 0000000000000000000000000000000000000000..5ea52128514c053d9db74b60e76d861d64e46476 GIT binary patch literal 580 zcmWIWW@Zs#;Nak3NGS>pXFvkH3@i+ZMJ1VOiOD6!dKI}j|4uP5^ah_Tx@91+@3V-N zL1@v6p4>?XuCxXJW|;frjK;M?IquU!{%#RyD__rZLM?Zp?EEdO-@DH*U;T93)FoHN7(e9Z7>zm(`3utB_S9jxtiBS#p2Ni z-A&@he_ilwThw%1Y|itwohC7D5ohF|`)`i?X)xD+{xXx@T{$J$Iu`7U+N-1=w5-^4 z@W{tIMafA$Cj)27+*D{>6IivubnkD0-WsLy-Y4fCN+tX5=G}CCK}*Z;YOj(HYCmT6 zeUDYr50f)eKFl`t^s#D@_Nx{3evc3AE4+RC|MWB3T6yK+B7SG>S^mxb^XWO8@(%WG zZ10-)ep^!f{o3qRI;vGROC3FbKGVE^MM1poMXB?Y1uHbI#1lf#oW7}5wJEBFO{aFd zALM_2=Hd-kYa8w*ucoZAi=@_E8r~o7S1H4(;KuQ>a&>l#?2O7)3007~|@^SzG literal 0 HcmV?d00001 diff --git a/tycho-its/repositories/content_jar/content.jar b/tycho-its/repositories/content_jar/content.jar new file mode 100644 index 0000000000000000000000000000000000000000..a69c2002cdb62c7d499b3fda2537cd2de4d62347 GIT binary patch literal 988 zcmWIWW@Zs#;Nak3NGS>pXFvkn3@i-E`FSO&c_n%kxjFw%F)+N1xSc0uC{TMo{70_z zN}caaqAIIqxvZMCFXU{ni0id?40&g7eO=m8Q+s@a_+}jzk5wUAN4YEXo}alIcl~Oc60T?B<}*>2hY$NsS#p%R*nXKTlUb{8>^$D%g2{lgKQ|g<^Ff+>htX z)>^|k_n^j(nh8#l9iD>X0-Gmf2xSR59hrGW;E?}mp^pNajx=~_@TY2@5jd=rS)r?R zYJW}ur;Xmho1BWyD_dAnB{CM>R9>`o+Ur(T6lEPn` z^eAypWBn5m8cJtGuFT0)xYn+cyx>@pd;fv5cC9lGTHBImuG*p+`72)MT=fl|uNQ7U zQr$Amea7z>P9}%KJ_&0>Xx9}iCbn|6MGlkT=rj1-K+D4+k!(|9zS&9 z>}Fn&VdQxK_LRcKSM$uG&ZaH5tk}J?`r=QOAIPaKsZgx?n z=4?+pZ*&*zJTAAFA$q!xg+#hs@VbQ(vf6=;3H3A9=zHH1U2EZL;GlO#XM@>#eQD!c zmCt(WRxSGNw7IJK<(KI(YqQkFTe%Nbwtje5X|XWW=J+fr<@?uPfBtFH!Y&fe=jZp@ zZmqJbg05W~vw)l z^Hh%$tx1zt-t;v4e#>`zfi}nT68mQs(VYC?_Zau zembeUUcP<4?#|sx$&-?%edKHMNY(M*y7<`Rqx;u1uybcdy0nX!SNbRiudJ9k(Pd87 z*<(Cc-Yweh_vxPM^O|j(Kc?l1Rvp^*e*W|)O!2a-vr2P=&5D`>u6CF5e`5aN_{_rXL&wc*C^^4T?ORuCimH7+btmjEr-_ID}&CcP{oN&O0 snSnt^kbxn{x6`*Cz0B=?{kOD>^v<1?gKtmZA0G|xBNB{r; literal 0 HcmV?d00001 diff --git a/tycho-its/repositories/content_jar/features/issue_2938_reproducer_1.0.0.202310211419.jar b/tycho-its/repositories/content_jar/features/issue_2938_reproducer_1.0.0.202310211419.jar new file mode 100644 index 0000000000000000000000000000000000000000..9c1b4111bae105dbc4f5bf84a5d3dfbb984437d4 GIT binary patch literal 356 zcmWIWW@Zs#;Nak3NGS>pXFvknKz3ScVo7OHs$NBIPVK~lU55-rT)+SAn(%jv@7z|e zXz{+L2SsVBN~X299A>plFIiP@FUh-K1t z-=FvD&b(aJ^NBlTopZ;D#~r&C9J2bzPhLlpa$P0HxjQf7gUV5`< zsbuo}9LLiy)#ra6cC&QYRBJb< zrgxc?Lq~uAp$Oi$ZtzqIL@bEVL} z4L$!D0=(HdnC{Ca-2r;}3nK%N&&VXgfCv_3IZ&{m0;E6+@MdKLNiqVVC6E>c8pgl? E0OQYrumAu6 literal 0 HcmV?d00001 diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/tycho2938/ContentJarTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/tycho2938/ContentJarTest.java new file mode 100644 index 0000000000..ac28b6d3d7 --- /dev/null +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/tycho2938/ContentJarTest.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2023, 2023 Sonatype Inc. and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vasili Gulevich - initial implementation + *******************************************************************************/ +package org.eclipse.tycho.test.tycho2938; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.eclipse.tycho.test.AbstractTychoIntegrationTest; +import org.eclipse.tycho.test.util.HttpServer; +import org.eclipse.tycho.test.util.ResourceUtil; +import org.eclipse.tycho.test.util.TargetDefinitionUtil; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class ContentJarTest extends AbstractTychoIntegrationTest { + private HttpServer server; + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void startServer() throws Exception { + server = HttpServer.startServer(); + File originalResource = ResourceUtil.resolveTestResource("repositories/content_jar"); + FileUtils.copyDirectory(originalResource, temporaryFolder.getRoot()); + } + + @After + public void stopServer() throws Exception { + if (server != null) { + server.stop(); + } + } + + private void mangleFileNames(Path repositoryRoot) throws IOException { + Files.walkFileTree(repositoryRoot, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.move(file, file.getParent().resolve(file.getFileName() + "_invalid")); + return super.visitFile(file, attrs); + } + }); + } + + @Test + public void noRedirect() throws Exception { + File repositoryRoot = temporaryFolder.getRoot(); + String repoUrl = server.addServer("repoA", repositoryRoot); + Verifier verifier = getVerifier("target.content_jar", false); + File platformFile = new File(verifier.getBasedir(), "targetplatform.target"); + TargetDefinitionUtil.setRepositoryURLs(platformFile, "repoA", repoUrl); + verifier.executeGoals(Arrays.asList("package")); + verifier.verifyErrorFreeLog(); + } + + @Test + public void redirectKeepFilename() throws Exception { + File repositoryRoot = temporaryFolder.getRoot(); + String repoUrl = server.addServer("repoA", repositoryRoot); + String redirectedUrl = server.addRedirect("repoB", originalPath -> repoUrl + originalPath); + Verifier verifier = getVerifier("target.content_jar", false); + File platformFile = new File(verifier.getBasedir(), "targetplatform.target"); + TargetDefinitionUtil.setRepositoryURLs(platformFile, "repoA", redirectedUrl); + verifier.executeGoals(Arrays.asList("package")); + verifier.verifyErrorFreeLog(); + } + + @Test + public void redirectToBadLocation() throws Exception { + File repositoryRoot = temporaryFolder.getRoot(); + String repoUrl = server.addServer("repoA", repositoryRoot); + String redirectedUrl = server.addRedirect("repoB", originalPath -> repoUrl + originalPath + "_invalid"); + Verifier verifier = getVerifier("target.content_jar", false); + File platformFile = new File(verifier.getBasedir(), "targetplatform.target"); + TargetDefinitionUtil.setRepositoryURLs(platformFile, "repoA", redirectedUrl); + Assert.assertThrows(VerificationException.class, () -> verifier.executeGoal("package")); + + verifier.verifyTextInLog("No repository found at " + redirectedUrl); + } + + @Test + public void redirectToMangledLocations() throws Exception { + File repositoryRoot = temporaryFolder.getRoot(); + mangleFileNames(repositoryRoot.toPath()); + String mangledRepoUrl = server.addServer("repoA", repositoryRoot); + + // https://github.com/eclipse-tycho/tycho/issues/2938 + // Redirect may change extension. + String originaRepoUrl = server.addRedirect("repoB", originalPath -> mangledRepoUrl + originalPath + "_invalid"); + + Verifier verifier = getVerifier("target.content_jar", false); + File platformFile = new File(verifier.getBasedir(), "targetplatform.target"); + TargetDefinitionUtil.setRepositoryURLs(platformFile, "repoA", originaRepoUrl); + verifier.executeGoals(Arrays.asList("package")); + verifier.verifyErrorFreeLog(); + } + +} diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/util/HttpServer.java b/tycho-its/src/test/java/org/eclipse/tycho/test/util/HttpServer.java index 682e6fb942..191606fc5c 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/util/HttpServer.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/util/HttpServer.java @@ -19,8 +19,10 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -74,6 +76,21 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } } + private static class RedirectServlet extends HttpServlet { + private final Function relativeUrlToNewUrl; + + public RedirectServlet(Function relativeUrlToNewUrl2) { + super(); + this.relativeUrlToNewUrl = relativeUrlToNewUrl2; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.sendRedirect(relativeUrlToNewUrl.apply(req.getServletPath())); + } + + } + private static final int BIND_ATTEMPTS = 20; private final Server server; @@ -155,16 +172,32 @@ public String addServer(String contextName, final File content) { ServletContextHandler context = new ServletContextHandler(contexts, URIUtil.SLASH + contextName); context.setResourceBase(content.getAbsolutePath()); + addMonitoring(contextName, context); + try { + context.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return getUrl(contextName); + } + + private void addMonitoring(String contextName, ServletContextHandler context) { MonitoringServlet monitoringServlet = new MonitoringServlet(); contextName2servletsMap.put(contextName, monitoringServlet); context.addServlet(new ServletHolder(monitoringServlet), URIUtil.SLASH); contexts.addHandler(context); + } + + public String addRedirect(String contextName, Function relativeUrlToNewUrl) { + ServletContextHandler context = new ServletContextHandler(contexts, URIUtil.SLASH + contextName); + context.addServlet(new ServletHolder(new RedirectServlet(relativeUrlToNewUrl)), URIUtil.SLASH); try { context.start(); } catch (Exception e) { throw new RuntimeException(e); } return getUrl(contextName); + } public String getUrl(String contextName) {