Merge pull request #569 from martinlau/Issue-556
* Issue-556: Escape URL characters in JAR URLs
This commit is contained in:
		
						commit
						403d57c695
					
				| 
						 | 
				
			
			@ -16,9 +16,11 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot.loader.jar;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.net.MalformedURLException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.util.jar.Manifest;
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +66,7 @@ class JarURLConnection extends java.net.JarURLConnection {
 | 
			
		|||
		 */
 | 
			
		||||
		if (separator + SEPARATOR.length() != spec.length()) {
 | 
			
		||||
			this.jarFileUrl = new URL("jar:" + spec);
 | 
			
		||||
			this.jarEntryName = spec.substring(separator + 2);
 | 
			
		||||
			this.jarEntryName = decode(spec.substring(separator + 2));
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			// The root of the archive (!/)
 | 
			
		||||
| 
						 | 
				
			
			@ -162,4 +164,39 @@ class JarURLConnection extends java.net.JarURLConnection {
 | 
			
		|||
		return builder.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static String decode(String source) {
 | 
			
		||||
		int length = source.length();
 | 
			
		||||
		if ((length == 0) || (source.indexOf('%') < 0)) {
 | 
			
		||||
			return source;
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			ByteArrayOutputStream bos = new ByteArrayOutputStream(length);
 | 
			
		||||
			for (int i = 0; i < length; i++) {
 | 
			
		||||
				int ch = source.charAt(i);
 | 
			
		||||
				if (ch == '%') {
 | 
			
		||||
					if ((i + 2) >= length) {
 | 
			
		||||
						throw new IllegalArgumentException("Invalid encoded sequence \""
 | 
			
		||||
								+ source.substring(i) + "\"");
 | 
			
		||||
					}
 | 
			
		||||
					ch = decodeEscapeSequence(source, i);
 | 
			
		||||
					i += 2;
 | 
			
		||||
				}
 | 
			
		||||
				bos.write(ch);
 | 
			
		||||
			}
 | 
			
		||||
			return new String(bos.toByteArray(), "UTF-8");
 | 
			
		||||
		}
 | 
			
		||||
		catch (UnsupportedEncodingException ex) {
 | 
			
		||||
			throw new IllegalStateException(ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static char decodeEscapeSequence(String source, int i) {
 | 
			
		||||
		int hi = Character.digit(source.charAt(i + 1), 16);
 | 
			
		||||
		int lo = Character.digit(source.charAt(i + 2), 16);
 | 
			
		||||
		if (hi == -1 || lo == -1) {
 | 
			
		||||
			throw new IllegalArgumentException("Invalid encoded sequence \""
 | 
			
		||||
					+ source.substring(i) + "\"");
 | 
			
		||||
		}
 | 
			
		||||
		return ((char) ((hi << 4) + lo));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,8 @@ public abstract class TestJarCreator {
 | 
			
		|||
			writeEntry(jarOutputStream, "2.dat", 2);
 | 
			
		||||
			writeDirEntry(jarOutputStream, "d/");
 | 
			
		||||
			writeEntry(jarOutputStream, "d/9.dat", 9);
 | 
			
		||||
			writeDirEntry(jarOutputStream, "special/");
 | 
			
		||||
			writeEntry(jarOutputStream, "special/\u00EB.dat", '\u00EB');
 | 
			
		||||
 | 
			
		||||
			JarEntry nestedEntry = new JarEntry("nested.jar");
 | 
			
		||||
			byte[] nestedJarData = getNestedJarData();
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +70,7 @@ public abstract class TestJarCreator {
 | 
			
		|||
		writeManifest(jarOutputStream, "j2");
 | 
			
		||||
		writeEntry(jarOutputStream, "3.dat", 3);
 | 
			
		||||
		writeEntry(jarOutputStream, "4.dat", 4);
 | 
			
		||||
		writeEntry(jarOutputStream, "\u00E4.dat", '\u00E4');
 | 
			
		||||
		jarOutputStream.close();
 | 
			
		||||
		return byteArrayOutputStream.toByteArray();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ public class ExplodedArchiveTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void getEntries() throws Exception {
 | 
			
		||||
		Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
 | 
			
		||||
		assertThat(entries.size(), equalTo(7));
 | 
			
		||||
		assertThat(entries.size(), equalTo(9));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ public class JarFileArchiveTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void getEntries() throws Exception {
 | 
			
		||||
		Map<String, Archive.Entry> entries = getEntriesMap(this.archive);
 | 
			
		||||
		assertThat(entries.size(), equalTo(7));
 | 
			
		||||
		assertThat(entries.size(), equalTo(9));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ import java.io.FileNotFoundException;
 | 
			
		|||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.net.URLClassLoader;
 | 
			
		||||
import java.util.Enumeration;
 | 
			
		||||
import java.util.jar.JarEntry;
 | 
			
		||||
import java.util.jar.Manifest;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +51,7 @@ import static org.mockito.Mockito.verify;
 | 
			
		|||
 * Tests for {@link JarFile}.
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @author Martin Lau
 | 
			
		||||
 */
 | 
			
		||||
public class JarFileTests {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +72,26 @@ public class JarFileTests {
 | 
			
		|||
		this.jarFile = new JarFile(this.rootJarFile);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void jdkJarFile() throws Exception {
 | 
			
		||||
		// Sanity checks to see how the default jar file operates
 | 
			
		||||
		java.util.jar.JarFile jarFile = new java.util.jar.JarFile(this.rootJarFile);
 | 
			
		||||
		Enumeration<java.util.jar.JarEntry> entries = jarFile.entries();
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("META-INF/"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("META-INF/MANIFEST.MF"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("1.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("2.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("d/"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("d/9.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("special/"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("special/\u00EB.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("nested.jar"));
 | 
			
		||||
		assertThat(entries.hasMoreElements(), equalTo(false));
 | 
			
		||||
		URL jarUrl = new URL("jar:" + this.rootJarFile.toURI() + "!/");
 | 
			
		||||
		URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { jarUrl });
 | 
			
		||||
		assertThat(urlClassLoader.getResource("special/\u00EB.dat"), notNullValue());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void createFromFile() throws Exception {
 | 
			
		||||
		JarFile jarFile = new JarFile(this.rootJarFile);
 | 
			
		||||
| 
						 | 
				
			
			@ -99,10 +121,19 @@ public class JarFileTests {
 | 
			
		|||
		assertThat(entries.nextElement().getName(), equalTo("2.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("d/"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("d/9.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("special/"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("special/\u00EB.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("nested.jar"));
 | 
			
		||||
		assertThat(entries.hasMoreElements(), equalTo(false));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getSpecialResourceViaClassLoader() throws Exception {
 | 
			
		||||
		URLClassLoader urlClassLoader = new URLClassLoader(
 | 
			
		||||
				new URL[] { this.jarFile.getUrl() });
 | 
			
		||||
		assertThat(urlClassLoader.getResource("special/\u00EB.dat"), notNullValue());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getJarEntry() throws Exception {
 | 
			
		||||
		java.util.jar.JarEntry entry = this.jarFile.getJarEntry("1.dat");
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +235,7 @@ public class JarFileTests {
 | 
			
		|||
		assertThat(entries.nextElement().getName(), equalTo("META-INF/MANIFEST.MF"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("3.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("4.dat"));
 | 
			
		||||
		assertThat(entries.nextElement().getName(), equalTo("\u00E4.dat"));
 | 
			
		||||
		assertThat(entries.hasMoreElements(), equalTo(false));
 | 
			
		||||
 | 
			
		||||
		InputStream inputStream = nestedJarFile.getInputStream(nestedJarFile
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue