-
Bug
-
Resolution: Fixed
-
P4
-
11, 13
-
b17
-
x86_64
-
windows_10
-
Not verified
ADDITIONAL SYSTEM INFORMATION :
C:\java>systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.18362 N/A Build 18362
C:\java>java --version
openjdk 13.0.1 2019-10-15
OpenJDK Runtime Environment (build 13.0.1+9)
OpenJDK 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
HttpRequest.BodyPublishers#ofFile(Path) assumes that the specified file lives in the default file system. If the file lives in a different file system, an UnsupportedOperationException is thrown.
This is because the current BodyPublisher implementation calls Path#toFile() on the file in an attempt to convert it to a java.io.File, and that throws UOE. This line throws:
https://github.com/openjdk/jdk/blob/5845912fdbffd09a8d8dd0e3f766137a4939e34e/src/java.net.http/share/classes/jdk/internal/net/http/RequestPublishers.java#L265
Theoretically there is no reason the implementation should need to convert the file to a java.io.File or otherwise assume the default file system is being used.
Use cases for non-default file systems in this context include:
(1) A zip file system, if you did Files.newFileSystem(zipFile) and wanted to upload one of the files contained within the zip file in an HTTP request.
(2) An in-memory file system like the one provided by the Jimfs library, which is useful during tests when you want to ensure that no debris is left on the real file system.
I ran into this while doing (2). To work around the issue I'm using BodyPublishers.ofInputStream instead.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Have a non-default FileSystem such as a zip file system or a Jimfs file system.
2. Have or create a file within that file system.
3. Invoke HttpRequest.BodyPublishers.ofFile(file).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
BodyPublishers.ofFile(file) should return a working BodyPublisher.
ACTUAL -
BodyPublishers.ofFile(file) throws UOE.
---------- BEGIN SOURCE ----------
// Maven coordinates of third-party libraries used:
//
// com.google.jimfs:jimfs:1.1
// io.undertow:undertow-core:2.0.28.Final
package example;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import io.undertow.Undertow;
import io.undertow.server.handlers.BlockingHandler;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Supplier;
import java.util.zip.ZipOutputStream;
public class FilePublisherTest {
public static void main(String[] args) throws Exception {
var requestHandler =
new BlockingHandler(
exchange -> {
var bytes = exchange.getInputStream().readAllBytes();
var string = new String(bytes, UTF_8);
System.out.println(
"\t\t...server received file with contents: \""
+ string
+ "\"");
});
var server =
Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(requestHandler)
.build();
server.start();
testDefaultFs();
testJimFs();
testZipFs();
server.stop();
}
// this works
private static void testDefaultFs() throws Exception {
var file = Files.createTempFile("FilePublisherTest", ".txt");
try {
Files.writeString(file, "default fs");
sendFile(file);
} finally {
Files.deleteIfExists(file);
}
}
// this fails, prints UOE stack trace
private static void testJimFs() throws Exception {
try (var fs = Jimfs.newFileSystem(Configuration.unix())) {
var file = fs.getPath("example.txt");
Files.writeString(file, "in-memory fs");
sendFile(file);
}
}
// this fails, prints UOE stack trace
private static void testZipFs() throws Exception {
var zipFile = Files.createTempFile("FilePublisherTest", ".zip");
try {
// create an empty zip file
try (var fos = Files.newOutputStream(zipFile);
var bos = new BufferedOutputStream(fos);
var zos = new ZipOutputStream(bos)) {}
try (var fs = FileSystems.newFileSystem(zipFile)) {
var file = fs.getPath("example.txt");
Files.writeString(file, "zip fs");
sendFile(file);
}
} finally {
Files.deleteIfExists(zipFile);
}
}
private static void sendFile(Path file)
throws IOException, InterruptedException {
Supplier<BodyPublisher> ofFile =
() -> {
try {
return BodyPublishers.ofFile(file);
} catch (FileNotFoundException e) {
throw new UncheckedIOException(e);
}
};
Supplier<BodyPublisher> ofInputStream =
() -> {
return BodyPublishers.ofInputStream(() -> {
try {
return Files.newInputStream(file);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
};
System.out.println("sending file");
System.out.println("\tfile: " + file);
System.out.println("\tfile system: " + file.getFileSystem());
System.out.println("\tfile contents: \"" + Files.readString(file) + "\"");
System.out.println("\ttrying BodyPublishers.ofFile...");
try {
sendFile(ofFile);
System.out.println("\t\t...success!");
} catch (UnsupportedOperationException e) {
System.out.println("\t\t...failed with UOE");
e.printStackTrace(System.out);
}
System.out.println("\ttrying BodyPublishers.ofInputStream...");
try {
sendFile(ofInputStream);
System.out.println("\t\t...success!");
} catch (UnsupportedOperationException e) {
System.out.println("\t\t...failed with UOE");
e.printStackTrace(System.out);
}
}
private static void sendFile(Supplier<BodyPublisher> supplier)
throws IOException, InterruptedException {
var client = HttpClient.newHttpClient();
var uri = URI.create("http://localhost:8080/file_publisher");
var publisher = supplier.get(); // might throw UOE
var request = HttpRequest.newBuilder(uri).POST(publisher).build();
var handler = HttpResponse.BodyHandlers.discarding();
var response = client.send(request, handler);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Open the Path as an InputStream and send it using BodyPublishers.ofInputStream.
C:\java>systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.18362 N/A Build 18362
C:\java>java --version
openjdk 13.0.1 2019-10-15
OpenJDK Runtime Environment (build 13.0.1+9)
OpenJDK 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
HttpRequest.BodyPublishers#ofFile(Path) assumes that the specified file lives in the default file system. If the file lives in a different file system, an UnsupportedOperationException is thrown.
This is because the current BodyPublisher implementation calls Path#toFile() on the file in an attempt to convert it to a java.io.File, and that throws UOE. This line throws:
https://github.com/openjdk/jdk/blob/5845912fdbffd09a8d8dd0e3f766137a4939e34e/src/java.net.http/share/classes/jdk/internal/net/http/RequestPublishers.java#L265
Theoretically there is no reason the implementation should need to convert the file to a java.io.File or otherwise assume the default file system is being used.
Use cases for non-default file systems in this context include:
(1) A zip file system, if you did Files.newFileSystem(zipFile) and wanted to upload one of the files contained within the zip file in an HTTP request.
(2) An in-memory file system like the one provided by the Jimfs library, which is useful during tests when you want to ensure that no debris is left on the real file system.
I ran into this while doing (2). To work around the issue I'm using BodyPublishers.ofInputStream instead.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Have a non-default FileSystem such as a zip file system or a Jimfs file system.
2. Have or create a file within that file system.
3. Invoke HttpRequest.BodyPublishers.ofFile(file).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
BodyPublishers.ofFile(file) should return a working BodyPublisher.
ACTUAL -
BodyPublishers.ofFile(file) throws UOE.
---------- BEGIN SOURCE ----------
// Maven coordinates of third-party libraries used:
//
// com.google.jimfs:jimfs:1.1
// io.undertow:undertow-core:2.0.28.Final
package example;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import io.undertow.Undertow;
import io.undertow.server.handlers.BlockingHandler;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Supplier;
import java.util.zip.ZipOutputStream;
public class FilePublisherTest {
public static void main(String[] args) throws Exception {
var requestHandler =
new BlockingHandler(
exchange -> {
var bytes = exchange.getInputStream().readAllBytes();
var string = new String(bytes, UTF_8);
System.out.println(
"\t\t...server received file with contents: \""
+ string
+ "\"");
});
var server =
Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(requestHandler)
.build();
server.start();
testDefaultFs();
testJimFs();
testZipFs();
server.stop();
}
// this works
private static void testDefaultFs() throws Exception {
var file = Files.createTempFile("FilePublisherTest", ".txt");
try {
Files.writeString(file, "default fs");
sendFile(file);
} finally {
Files.deleteIfExists(file);
}
}
// this fails, prints UOE stack trace
private static void testJimFs() throws Exception {
try (var fs = Jimfs.newFileSystem(Configuration.unix())) {
var file = fs.getPath("example.txt");
Files.writeString(file, "in-memory fs");
sendFile(file);
}
}
// this fails, prints UOE stack trace
private static void testZipFs() throws Exception {
var zipFile = Files.createTempFile("FilePublisherTest", ".zip");
try {
// create an empty zip file
try (var fos = Files.newOutputStream(zipFile);
var bos = new BufferedOutputStream(fos);
var zos = new ZipOutputStream(bos)) {}
try (var fs = FileSystems.newFileSystem(zipFile)) {
var file = fs.getPath("example.txt");
Files.writeString(file, "zip fs");
sendFile(file);
}
} finally {
Files.deleteIfExists(zipFile);
}
}
private static void sendFile(Path file)
throws IOException, InterruptedException {
Supplier<BodyPublisher> ofFile =
() -> {
try {
return BodyPublishers.ofFile(file);
} catch (FileNotFoundException e) {
throw new UncheckedIOException(e);
}
};
Supplier<BodyPublisher> ofInputStream =
() -> {
return BodyPublishers.ofInputStream(() -> {
try {
return Files.newInputStream(file);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
};
System.out.println("sending file");
System.out.println("\tfile: " + file);
System.out.println("\tfile system: " + file.getFileSystem());
System.out.println("\tfile contents: \"" + Files.readString(file) + "\"");
System.out.println("\ttrying BodyPublishers.ofFile...");
try {
sendFile(ofFile);
System.out.println("\t\t...success!");
} catch (UnsupportedOperationException e) {
System.out.println("\t\t...failed with UOE");
e.printStackTrace(System.out);
}
System.out.println("\ttrying BodyPublishers.ofInputStream...");
try {
sendFile(ofInputStream);
System.out.println("\t\t...success!");
} catch (UnsupportedOperationException e) {
System.out.println("\t\t...failed with UOE");
e.printStackTrace(System.out);
}
}
private static void sendFile(Supplier<BodyPublisher> supplier)
throws IOException, InterruptedException {
var client = HttpClient.newHttpClient();
var uri = URI.create("http://localhost:8080/file_publisher");
var publisher = supplier.get(); // might throw UOE
var request = HttpRequest.newBuilder(uri).POST(publisher).build();
var handler = HttpResponse.BodyHandlers.discarding();
var response = client.send(request, handler);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Open the Path as an InputStream and send it using BodyPublishers.ofInputStream.
- csr for
-
JDK-8240526 HttpRequest.BodyPublishers#ofFile(Path) assumes the default file system
- Closed
- relates to
-
JDK-8246431 java/net/httpclient/PathSubscriber tests fail due to missing FilePermission
- Resolved
-
JDK-8241674 Fix incorrect jtreg option in FilePublisherPermsTest
- Resolved
-
JDK-8237470 HttpResponse.BodySubscriber::ofFile throws UOE with non-default file systems
- Closed