From 8fed739c8164ca52b1a4818f14e4448a2f626ede Mon Sep 17 00:00:00 2001 From: Vitaly Parfonov Date: Thu, 19 May 2016 18:45:05 +0300 Subject: [PATCH 1/2] CODENVY-538:Fix checking of recipe location: now will match only host wihout scheme. Add method for download file in case server send redirect(in some reason setInstanceFollowRedirects(true) don't work so need to do one more request) Signed-off-by: Vitaly Parfonov --- .../org/eclipse/che/commons/lang/IoUtil.java | 75 ++++++++++++++++++- .../machine/server/util/RecipeDownloader.java | 16 ++-- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java index 93efe939d65..bbfbbdc74e8 100644 --- a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java +++ b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/IoUtil.java @@ -13,12 +13,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; +import javax.ws.rs.HttpMethod; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.nio.channels.FileChannel; -import java.nio.file.*; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.DigestInputStream; import java.security.MessageDigest; @@ -28,8 +41,6 @@ import java.util.List; import java.util.Locale; -import javax.ws.rs.HttpMethod; - import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.FileVisitResult.TERMINATE; @@ -231,6 +242,62 @@ public static File downloadFile(File parent, String prefix, String suffix, URL u return file; } + + + /** + * Download file with redirection if got status 301, 302, 303. + * Will useful in case redirection http -> https + * + * @param parent + * parent directory, may be null then use 'java.io.tmpdir' + * @param prefix + * prefix of temporary file name, may not be null and must be at least three characters long + * @param suffix + * suffix of temporary file name, may be null + * @param url + * URL for download + * @return downloaded file + * @throws java.io.IOException + * if any i/o error occurs + */ + public static File downloadFileWithRedirect(File parent, String prefix, String suffix, URL url) throws IOException { + File file = File.createTempFile(prefix, suffix, parent); + URLConnection conn = null; + final String protocol = url.getProtocol().toLowerCase(Locale.ENGLISH); + try { + conn = url.openConnection(); + boolean redirect = false; + if ("http".equals(protocol) || "https".equals(protocol)) { + HttpURLConnection http = (HttpURLConnection)conn; + http.setRequestMethod(HttpMethod.GET); + int status = http.getResponseCode(); + if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || + status == HttpURLConnection.HTTP_SEE_OTHER) { + redirect = true; + } + if (redirect) { + String newUrl = conn.getHeaderField("Location"); + // open the new connection again + http = (HttpURLConnection)new URL(newUrl).openConnection(); + http.setRequestMethod(HttpMethod.GET); + } + } + try (InputStream input = conn.getInputStream(); + FileOutputStream fOutput = new FileOutputStream(file)) { + byte[] b = new byte[8192]; + int r; + while ((r = input.read(b)) != -1) { + fOutput.write(b, 0, r); + } + } + } finally { + if (conn != null && ("http".equals(protocol) || "https".equals(protocol))) { + ((HttpURLConnection)conn).disconnect(); + } + } + return file; + } + /** * Copy file or directory to the specified destination. Existed files in destination directory will be overwritten. * diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java index db4139c0176..4798e9c56ce 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.net.URI; import java.net.URL; import static java.lang.String.format; @@ -39,10 +40,10 @@ public class RecipeDownloader { private static final Logger LOG = getLogger(RecipeDownloader.class); - private final String apiEndpoint; + private final URI apiEndpoint; @Inject - public RecipeDownloader(@Named("api.endpoint") String apiEndpoint) { + public RecipeDownloader(@Named("api.endpoint") URI apiEndpoint) { this.apiEndpoint = apiEndpoint; } @@ -58,24 +59,27 @@ public RecipeDownloader(@Named("api.endpoint") String apiEndpoint) { public RecipeImpl getRecipe(MachineConfig machineConfig) throws MachineException { URL recipeUrl; File file = null; + final String location = machineConfig.getSource().getLocation(); try { - UriBuilder targetUriBuilder = UriBuilder.fromUri(machineConfig.getSource().getLocation()); + UriBuilder targetUriBuilder = UriBuilder.fromUri(location); // add user token to be able to download user's private recipe - if (machineConfig.getSource().getLocation().startsWith(apiEndpoint)) { + final String apiEndPointHost = apiEndpoint.getHost(); + final String host = UriBuilder.fromUri(location).build().getHost(); + if (host.equals(apiEndPointHost)) { if (EnvironmentContext.getCurrent().getSubject() != null && EnvironmentContext.getCurrent().getSubject().getToken() != null) { targetUriBuilder.queryParam("token", EnvironmentContext.getCurrent().getSubject().getToken()); } } recipeUrl = targetUriBuilder.build().toURL(); - file = IoUtil.downloadFile(null, "recipe", null, recipeUrl); + file = IoUtil.downloadFileWithRedirect(null, "recipe", null, recipeUrl); return new RecipeImpl().withType(machineConfig.getSource().getType()) .withScript(IoUtil.readAndCloseQuietly(new FileInputStream(file))); } catch (IOException | IllegalArgumentException e) { throw new MachineException(format("Can't start machine %s because machine recipe downloading failed. Recipe url %s. Error: %s", machineConfig.getName(), - machineConfig.getSource().getLocation(), + location, e.getLocalizedMessage())); } finally { if (file != null && !file.delete()) { From acd21d022e91d9bc59bf560198c76a7e11751e19 Mon Sep 17 00:00:00 2001 From: Vitaly Parfonov Date: Fri, 20 May 2016 12:02:09 +0300 Subject: [PATCH 2/2] Use targetUriBuilder Signed-off-by: Vitaly Parfonov --- .../eclipse/che/api/machine/server/util/RecipeDownloader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java index 4798e9c56ce..066f626f0fd 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/util/RecipeDownloader.java @@ -64,8 +64,8 @@ public RecipeImpl getRecipe(MachineConfig machineConfig) throws MachineException UriBuilder targetUriBuilder = UriBuilder.fromUri(location); // add user token to be able to download user's private recipe final String apiEndPointHost = apiEndpoint.getHost(); - final String host = UriBuilder.fromUri(location).build().getHost(); - if (host.equals(apiEndPointHost)) { + final String host = targetUriBuilder.build().getHost(); + if (apiEndPointHost.equals(host)) { if (EnvironmentContext.getCurrent().getSubject() != null && EnvironmentContext.getCurrent().getSubject().getToken() != null) { targetUriBuilder.queryParam("token", EnvironmentContext.getCurrent().getSubject().getToken());