Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions test/jdk/sun/net/www/protocol/file/NonLocalFtpFallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2025, 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.
*/

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @test
* @bug 8353662
* @summary Verify long-standing behavior of resolving non-local file URLs using FTP.
* @run junit NonLocalFtpFallback
*/

public class NonLocalFtpFallback {

// Port 21 may not be available, use an HTTP proxy with an ephemeral port
private HttpServer proxyServer;

// The file requested in this test
private Path file;

// FTP URIs requested by the proxy client
private Set<URI> uris = new HashSet<>();

/**
* Set up the HTTP proxy used for serving FTP in this test
*
* @throws IOException if an unexpected IO error occurs
*/
@BeforeEach
public void setup() throws IOException {
// Create a file with some random data
byte[] data = new byte[512];
new Random().nextBytes(data);
file = Files.write(Path.of("ftp-file.txt"), data);

// Set up an HTTP proxy server
proxyServer = HttpServer.create();
// Bind to the loopback address with an ephemeral port
InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
proxyServer.bind(new InetSocketAddress(loopbackAddress, 0), 0);
// Handler for the FTP proxy request
proxyServer.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
// Record the URI requested
uris.add(exchange.getRequestURI());
// Send the data
exchange.sendResponseHeaders(200, Files.size(file));
try (OutputStream out = exchange.getResponseBody()) {
Files.copy(file, out);
}
// Complete the exchange
exchange.close();
}
});
// Start the proxy server
proxyServer.start();
}

/**
* Shut down proxy server and clean up files created
*
* @throws IOException if an unexpected IO error occurs
*/
@AfterEach
public void destroy() throws IOException {
proxyServer.stop(2);
Files.delete(file);
}

/**
* Verifies the long-standing and unspecified FTP fallback feature where the file
* URL scheme handler attempts an FTP connection for non-local files.
*
* The non-local file URL used here is of the form file://127.0.0.1/path. Since the
* host component here is not equal to "localhost", this is considered a non-local
* URL.
*
* @throws Exception
*/
@Test
public void verifyNonLocalFtpFallback() throws Exception {
URL localURL = file.toUri().toURL();
// We can use a fake host name here, no actual FTP request will be made
String hostname = "remotehost";
URL nonLocalURL = new URL("file", hostname, localURL.getFile());

// Open the non-local file: URL connection using a proxy
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyServer.getAddress().getAddress(),
proxyServer.getAddress().getPort()));
URLConnection con = nonLocalURL.openConnection(proxy);

// Assert that the expected file content is retrieved
try (InputStream in = con.getInputStream()) {
byte[] retrived = in.readAllBytes();
assertArrayEquals(Files.readAllBytes(file), retrived);
}

// Assert that the expected FTP URI was requested in the HTTP proxy
assertEquals(1, uris.size());
URL ftpURL = new URL("ftp", hostname, localURL.getFile());
assertEquals(ftpURL.toURI(), uris.iterator().next());
}
}