jdk-24/test/jdk/java/net/URL/URLFromURITest.java
2023-01-20 17:00:53 +00:00

378 lines
15 KiB
Java

/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8294241
* @library /test/lib
* @modules java.base/java.net:+open
* @summary Test URL::fromURI(URI, URLStreamHandler)
* @run junit/othervm URLFromURITest
*/
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.VarHandle;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.Locale;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import jdk.test.lib.RandomFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class URLFromURITest {
static final Random RAND = RandomFactory.getRandom();
record TestInput(String uri, URLStreamHandler handler) {
static TestInput withNoHandler(String uri) {
return new TestInput(uri, null);
}
TestInput withCustomHandler() {
return new TestInput(uri(), new CustomStreamHandler());
}
TestInput withUrlPrefix() {return inputWithUrlPrefix(this);}
}
static URI uriWithUrlPrefix(URI uri) {
return URI.create(stringWithUrlPrefix(uri.toString()));
}
static String stringWithUrlPrefix(String uriStr) {
if (uriStr.regionMatches(true, 0, "url:", 0, 4)) return uriStr;
return RAND.nextBoolean() ? "url:" + uriStr : "Url:" + uriStr;
}
static TestInput inputWithUrlPrefix(TestInput input) {
String uriStr = input.uri();
var handler = input.handler();
var urlUriStr = stringWithUrlPrefix(uriStr);
if (uriStr.equals(urlUriStr)) return null;
var urlURI = URI.create(urlUriStr);
try {
new URL(null, urlURI.toString(), handler);
} catch (Throwable t) {
System.err.println("skipping new URL(null, \"" + urlURI + "\", handler): " + t);
return null;
}
return new TestInput(urlUriStr, handler);
}
static Stream<String> uris() {
var uris = Stream.of(
"http://jag:cafebabe@java.sun.com:94/b/c/d?q#g",
"http://[1080:0:0:0:8:800:200C:417A]/index.html",
"http://a/b/c/d;p?q",
"mailto:mduerst@ifi.unizh.ch",
"http:comp.infosystems.www.servers.unix",
"http://j%41g:cafeb%41be@java.sun.com:94/%41/b/c/d?q#g",
"jar:file:///x.jar!/",
"jmod:/java.base",
"jmod:///java.base");
if (hasFtp()) {
uris = Stream.concat(uris,
Stream.of("ftp://ftp.is.co.za/rfc/rfc1808.txt"));
}
return uris;
}
static Stream<String> nonOverridableUris() {
return Stream.of("file:///nohost/%41/",
"file://with.host/%41/",
"file:/x/y/z",
"jrt:/java.base/java/lang/Integer.class",
"jrt:///java.base/java/lang/Integer.class");
}
static Stream<TestInput> withNoHandler() {
return Stream.concat(uris(), nonOverridableUris())
.map(TestInput::withNoHandler);
}
static Stream<TestInput> withCustomHandler() {
var withHandlers = uris()
.map(TestInput::withNoHandler)
.map(TestInput::withCustomHandler);
return Stream.concat(withHandlers, Stream.of(
new TestInput("foo:bar:baz", new CustomStreamHandler()),
new TestInput("jar:file:///x.jar!/", new CustomStreamHandler()),
new TestInput("jar:jar:file///x.jar!/bing", new CustomStreamHandler()),
new TestInput("blah://localhost:80/x/y/z", new CustomStreamHandler())
));
}
static Stream<TestInput> overridingNonOverridable() {
return nonOverridableUris().map(TestInput::withNoHandler)
.map(TestInput::withCustomHandler);
}
@Test
public void checkExceptions() {
var noscheme = URI.create("http");
var unknown = URI.create("unknown:///foo/bar");
var opaque = URI.create("opaque:opaque-path");
var jrt = URI.create("jrt:/java.base/java.lang.Integer.class");
var file = URI.create("file:/");
var unoscheme = uriWithUrlPrefix(noscheme);
var uunknown = uriWithUrlPrefix(unknown);
var uopaque = uriWithUrlPrefix(opaque);
var ujrt = uriWithUrlPrefix(jrt);
var ufile = uriWithUrlPrefix(file);
var handler = new CustomStreamHandler();
assertThrows(NullPointerException.class, () -> URL.of(null, null));
assertThrows(NullPointerException.class, () -> URL.of(null, handler));
assertThrows(IllegalArgumentException.class, () -> URL.of(noscheme, null));
assertThrows(IllegalArgumentException.class, () -> URL.of(noscheme, handler));
assertThrows(IllegalArgumentException.class, () -> URL.of(jrt, handler));
assertThrows(IllegalArgumentException.class, () -> URL.of(file, handler));
assertThrows(IllegalArgumentException.class, () -> URL.of(ujrt, handler));
assertThrows(IllegalArgumentException.class, () -> URL.of(ufile, handler));
assertThrows(MalformedURLException.class, () -> URL.of(unknown, null));
assertThrows(MalformedURLException.class, () -> URL.of(opaque, null));
assertThrows(MalformedURLException.class, () -> URL.of(uunknown, null));
assertThrows(MalformedURLException.class, () -> URL.of(uopaque, null));
assertThrows(MalformedURLException.class, () -> URL.of(unoscheme, null));
assertThrows(MalformedURLException.class, () -> URL.of(unoscheme, handler));
}
@ParameterizedTest
@MethodSource(value = "withNoHandler")
public void testWithNoHandler(TestInput input) throws Exception {
String uriStr = input.uri();
URLStreamHandler handler = input.handler();
System.err.println("testWithNoHandler: " + uriStr);
assertNull(handler, input + ": input handler");
URI uri = new URI(uriStr);
URL url = URL.of(uri, handler);
checkNoHandler(input, uri, url);
var urlInput = input.withUrlPrefix();
if (urlInput != null) {
try {
var urlURI = URI.create(input.uri());
checkNoHandler(urlInput, uri, URL.of(urlURI, null));
} catch (Throwable x) {
throw new AssertionError("Failed: " + urlInput.uri() + " with: " + x, x);
}
}
}
private void checkNoHandler(TestInput input, URI uri, URL url) throws Exception {
System.err.println("Testing: " + uri);
checkURL(input, uri, url);
URLStreamHandler urlHandler = URLAccess.getHandler(url);
assertNotNull(urlHandler, input + ": URL.handler");
assertNull(urlHandler.getClass().getClassLoader(),
input + ": URL.handler class loader");
}
@ParameterizedTest
@MethodSource(value = "withCustomHandler")
public void checkCustomHandler(TestInput input) throws Exception {
String uriStr = input.uri();
URLStreamHandler handler = input.handler();
System.err.println("testWithCustomHandler: " + input);
assertNotNull(handler, input + ": input handler");
URI uri = new URI(uriStr);
URL url = URL.of(uri, handler);
checkCustomHandler(input, uri, url, handler);
var urlInput = input.withUrlPrefix();
if (urlInput != null) {
urlInput = urlInput.withCustomHandler();
handler = urlInput.handler();
try {
var urlURI = URI.create(urlInput.uri());
checkCustomHandler(urlInput, uri, URL.of(urlURI, handler), handler);
} catch (Throwable x) {
throw new AssertionError("Failed with handler: " + urlInput.uri() + " with: " + x, x);
}
}
}
private void checkCustomHandler(TestInput input, URI uri, URL url,
URLStreamHandler handler) throws Exception {
System.err.println("Testing: " + uri);
checkURL(input, uri, url);
URLStreamHandler urlHandler = URLAccess.getHandler(url);
assertSame(handler, urlHandler, input + ": URL.handler");
URLConnection c = url.openConnection();
assertNotNull(c, input + ": opened connection");
assertEquals(CustomURLConnection.class, c.getClass(),
input + ": connection class");
assertEquals(CustomStreamHandler.class, urlHandler.getClass(),
input + ": handler class");
assertEquals(((CustomURLConnection)c).handler, handler, input + ": handler");
assertEquals(c.getURL(), url, input + ": connection url");
var customHandler = (CustomStreamHandler)urlHandler;
assertEquals(customHandler.parseURLCalled(), 1, "parseURL calls");
}
@ParameterizedTest
@MethodSource(value = "overridingNonOverridable")
public void testOverridingNonOverridable(TestInput input) throws Exception {
String uriStr = input.uri();
URLStreamHandler handler = input.handler();
System.err.println("testOverridingNonOverridable: " + input);
assertNotNull(handler, input + ": input handler");
URI uri = new URI(uriStr);
try {
URL url = URL.of(uri, handler);
throw new AssertionError("Should not be able to specify handler for: " + uriStr);
} catch (IllegalArgumentException x) {
System.err.println("Got expected exception: " + x);
}
}
private static boolean isFileBased(URI uri) {
String scheme = uri.getScheme();
boolean isJrt = "jrt".equals(scheme.toLowerCase(Locale.ROOT));
boolean isJmod = "jmod".equals(scheme.toLowerCase(Locale.ROOT));
boolean isFile = "file".equals(scheme.toLowerCase(Locale.ROOT));
return isJmod || isJrt || isFile;
}
private static void checkURL(TestInput input, URI uri, URL url) throws MalformedURLException {
String scheme = uri.getScheme();
assertEquals(scheme, url.getProtocol(), input + ": scheme");
if (uri.isOpaque()) {
String ssp = uri.getSchemeSpecificPart();
assertEquals(ssp, url.getPath(), input + ": ssp");
} else {
String authority = uri.getRawAuthority();
boolean isHierarchical = uri.toString().startsWith(scheme + "://");
boolean isFileBased = isFileBased(uri);
// Network based URLs usually follow URI, but file based
// protocol handlers have a few discrepancies in how they
// treat an absent authority:
// - URI authority is null if there is no authority, always
// - URL authority is null or empty depending on the protocol
// and on whether the URL is hierarchical or not.
if (isFileBased && authority == null) {
// jrt: takes a fastpath - so that jrt:/ is equivalent to jrt:///
if (scheme.equals("jrt")) {
authority = "";
}
if (isHierarchical) {
authority = "";
}
}
assertEquals(authority, url.getAuthority(), input + ": authority");
// Network based URLs usually follow URI, but file based
// protocol handlers have a few discrepancies in how they
// treat an absent host:
String host = uri.getHost();
if (isFileBased && host == null) {
host = "";
}
assertEquals(host, url.getHost(), input + ": host");
if (host != null) {
String userInfo = uri.getRawUserInfo();
assertEquals(userInfo, url.getUserInfo(), input + ": userInfo");
assertEquals(uri.getPort(), url.getPort(), input + ": port");
}
String path = uri.getRawPath();
assertEquals(path, url.getPath(), input + ": path");
String query = uri.getQuery();
assertEquals(query, url.getQuery(), input + ": query");
}
String frag = uri.getRawFragment();
assertEquals(frag, url.getRef(), input + ": fragment");
}
@SuppressWarnings("deprecation")
private static boolean hasFtp() {
try {
return new java.net.URL("ftp://localhost/") != null;
} catch (java.net.MalformedURLException x) {
System.err.println("FTP not supported by this runtime.");
return false;
}
}
static class CustomURLConnection extends URLConnection {
public final CustomStreamHandler handler;
CustomURLConnection(CustomStreamHandler handler, URL url) {
super(url);
this.handler = handler;
}
@Override
public void connect() throws IOException {
}
}
static class CustomStreamHandler extends URLStreamHandler {
final AtomicInteger parseURLCalled = new AtomicInteger();
@Override
protected void parseURL(URL u, String spec, int start, int limit) {
parseURLCalled.incrementAndGet();
super.parseURL(u, spec, start, limit);
}
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new CustomURLConnection(this, u);
}
public int parseURLCalled() {
return parseURLCalled.get();
}
}
static final class URLAccess {
static final VarHandle HANDLER;
static {
try {
Lookup lookup = MethodHandles.privateLookupIn(URL.class, MethodHandles.lookup());
HANDLER = lookup.findVarHandle(URL.class, "handler", URLStreamHandler.class);
} catch (Exception x) {
throw new ExceptionInInitializerError(x);
}
}
static URLStreamHandler getHandler(URL url) {
return (URLStreamHandler)HANDLER.get(url);
}
}
}