Skip to content

Commit cae7a20

Browse files
author
Eirik Bjørsnøs
committed
8353662: Add test for non-local file URL fallback to FTP
Reviewed-by: dfuchs
1 parent b5d2e25 commit cae7a20

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import com.sun.net.httpserver.HttpExchange;
25+
import com.sun.net.httpserver.HttpHandler;
26+
import com.sun.net.httpserver.HttpServer;
27+
import org.junit.jupiter.api.AfterEach;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
31+
import java.io.IOException;
32+
import java.io.InputStream;
33+
import java.io.OutputStream;
34+
import java.net.*;
35+
import java.nio.file.Files;
36+
import java.nio.file.Path;
37+
import java.util.HashSet;
38+
import java.util.Random;
39+
import java.util.Set;
40+
import java.util.concurrent.ExecutorService;
41+
import java.util.concurrent.Executors;
42+
43+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
44+
import static org.junit.jupiter.api.Assertions.assertEquals;
45+
46+
/**
47+
* @test
48+
* @bug 8353662
49+
* @summary Verify long-standing behavior of resolving non-local file URLs using FTP.
50+
* @run junit NonLocalFtpFallback
51+
*/
52+
53+
public class NonLocalFtpFallback {
54+
55+
// Port 21 may not be available, use an HTTP proxy with an ephemeral port
56+
private HttpServer proxyServer;
57+
58+
// The file requested in this test
59+
private Path file;
60+
61+
// FTP URIs requested by the proxy client
62+
private Set<URI> uris = new HashSet<>();
63+
64+
/**
65+
* Set up the HTTP proxy used for serving FTP in this test
66+
*
67+
* @throws IOException if an unexpected IO error occurs
68+
*/
69+
@BeforeEach
70+
public void setup() throws IOException {
71+
// Create a file with some random data
72+
byte[] data = new byte[512];
73+
new Random().nextBytes(data);
74+
file = Files.write(Path.of("ftp-file.txt"), data);
75+
76+
// Set up an HTTP proxy server
77+
proxyServer = HttpServer.create();
78+
// Bind to the loopback address with an ephemeral port
79+
InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
80+
proxyServer.bind(new InetSocketAddress(loopbackAddress, 0), 0);
81+
// Handler for the FTP proxy request
82+
proxyServer.createContext("/", new HttpHandler() {
83+
@Override
84+
public void handle(HttpExchange exchange) throws IOException {
85+
// Record the URI requested
86+
uris.add(exchange.getRequestURI());
87+
// Send the data
88+
exchange.sendResponseHeaders(200, Files.size(file));
89+
try (OutputStream out = exchange.getResponseBody()) {
90+
Files.copy(file, out);
91+
}
92+
// Complete the exchange
93+
exchange.close();
94+
}
95+
});
96+
// Start the proxy server
97+
proxyServer.start();
98+
}
99+
100+
/**
101+
* Shut down proxy server and clean up files created
102+
*
103+
* @throws IOException if an unexpected IO error occurs
104+
*/
105+
@AfterEach
106+
public void destroy() throws IOException {
107+
proxyServer.stop(2);
108+
Files.delete(file);
109+
}
110+
111+
/**
112+
* Verifies the long-standing and unspecified FTP fallback feature where the file
113+
* URL scheme handler attempts an FTP connection for non-local files.
114+
*
115+
* The non-local file URL used here is of the form file://127.0.0.1/path. Since the
116+
* host component here is not equal to "localhost", this is considered a non-local
117+
* URL.
118+
*
119+
* @throws Exception
120+
*/
121+
@Test
122+
public void verifyNonLocalFtpFallback() throws Exception {
123+
URL localURL = file.toUri().toURL();
124+
// We can use a fake host name here, no actual FTP request will be made
125+
String hostname = "remotehost";
126+
URL nonLocalURL = new URL("file", hostname, localURL.getFile());
127+
128+
// Open the non-local file: URL connection using a proxy
129+
Proxy proxy = new Proxy(Proxy.Type.HTTP,
130+
new InetSocketAddress(proxyServer.getAddress().getAddress(),
131+
proxyServer.getAddress().getPort()));
132+
URLConnection con = nonLocalURL.openConnection(proxy);
133+
134+
// Assert that the expected file content is retrieved
135+
try (InputStream in = con.getInputStream()) {
136+
byte[] retrived = in.readAllBytes();
137+
assertArrayEquals(Files.readAllBytes(file), retrived);
138+
}
139+
140+
// Assert that the expected FTP URI was requested in the HTTP proxy
141+
assertEquals(1, uris.size());
142+
URL ftpURL = new URL("ftp", hostname, localURL.getFile());
143+
assertEquals(ftpURL.toURI(), uris.iterator().next());
144+
}
145+
}

0 commit comments

Comments
 (0)