11/*
2- * Copyright (c) 2016, 2019 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2016, 2020 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2525
2626package jdk .internal .net .http ;
2727
28- import java .io .File ;
2928import java .io .FileInputStream ;
3029import java .io .FileNotFoundException ;
3130import java .io .FilePermission ;
3231import java .io .IOException ;
3332import java .io .InputStream ;
3433import java .io .UncheckedIOException ;
34+ import java .lang .reflect .UndeclaredThrowableException ;
3535import java .nio .ByteBuffer ;
3636import java .nio .charset .Charset ;
3737import java .nio .file .Files ;
3838import java .nio .file .Path ;
3939import java .security .AccessControlContext ;
4040import java .security .AccessController ;
41- import java .security .PrivilegedAction ;
41+ import java .security .Permission ;
4242import java .security .PrivilegedActionException ;
4343import java .security .PrivilegedExceptionAction ;
4444import java .util .ArrayList ;
5050import java .util .concurrent .ConcurrentLinkedQueue ;
5151import java .util .concurrent .Flow ;
5252import java .util .concurrent .Flow .Publisher ;
53+ import java .util .function .Function ;
5354import java .util .function .Supplier ;
5455import java .net .http .HttpRequest .BodyPublisher ;
5556import jdk .internal .net .http .common .Utils ;
@@ -220,17 +221,17 @@ public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
220221
221222 /**
222223 * Publishes the content of a given file.
223- *
224+ * <p>
224225 * Privileged actions are performed within a limited doPrivileged that only
225226 * asserts the specific, read, file permission that was checked during the
226- * construction of this FilePublisher.
227+ * construction of this FilePublisher. This only applies if the file system
228+ * that created the file provides interoperability with {@code java.io.File}.
227229 */
228- public static class FilePublisher implements BodyPublisher {
230+ public static class FilePublisher implements BodyPublisher {
229231
230- private static final FilePermission [] EMPTY_FILE_PERMISSIONS = new FilePermission [0 ];
231-
232- private final File file ;
233- private final FilePermission [] filePermissions ;
232+ private final Path path ;
233+ private final long length ;
234+ private final Function <Path , InputStream > inputStreamSupplier ;
234235
235236 private static String pathForSecurityCheck (Path path ) {
236237 return path .toFile ().getPath ();
@@ -243,48 +244,112 @@ private static String pathForSecurityCheck(Path path) {
243244 * FilePublisher. Permission checking and construction are deliberately
244245 * and tightly co-located.
245246 */
246- public static FilePublisher create (Path path ) throws FileNotFoundException {
247- FilePermission filePermission = null ;
247+ public static FilePublisher create (Path path )
248+ throws FileNotFoundException {
248249 SecurityManager sm = System .getSecurityManager ();
249- if (sm != null ) {
250+ FilePermission filePermission = null ;
251+ boolean defaultFS = true ;
252+
253+ try {
250254 String fn = pathForSecurityCheck (path );
251- FilePermission readPermission = new FilePermission (fn , "read" );
252- sm .checkPermission (readPermission );
253- filePermission = readPermission ;
255+ if (sm != null ) {
256+ FilePermission readPermission = new FilePermission (fn , "read" );
257+ sm .checkPermission (readPermission );
258+ filePermission = readPermission ;
259+ }
260+ } catch (UnsupportedOperationException uoe ) {
261+ defaultFS = false ;
262+ // Path not associated with the default file system
263+ // Test early if an input stream can still be obtained
264+ try {
265+ if (sm != null ) {
266+ Files .newInputStream (path ).close ();
267+ }
268+ } catch (IOException ioe ) {
269+ if (ioe instanceof FileNotFoundException ) {
270+ throw (FileNotFoundException ) ioe ;
271+ } else {
272+ var ex = new FileNotFoundException (ioe .getMessage ());
273+ ex .initCause (ioe );
274+ throw ex ;
275+ }
276+ }
254277 }
255278
256279 // existence check must be after permission checks
257280 if (Files .notExists (path ))
258281 throw new FileNotFoundException (path + " not found" );
259282
260- return new FilePublisher (path , filePermission );
283+ Permission perm = filePermission ;
284+ assert perm == null || perm .getActions ().equals ("read" );
285+ AccessControlContext acc = sm != null ?
286+ AccessController .getContext () : null ;
287+ boolean finalDefaultFS = defaultFS ;
288+ Function <Path , InputStream > inputStreamSupplier = (p ) ->
289+ createInputStream (p , acc , perm , finalDefaultFS );
290+
291+ long length ;
292+ try {
293+ length = Files .size (path );
294+ } catch (IOException ioe ) {
295+ length = -1 ;
296+ }
297+
298+ return new FilePublisher (path , length , inputStreamSupplier );
299+ }
300+
301+ private static InputStream createInputStream (Path path ,
302+ AccessControlContext acc ,
303+ Permission perm ,
304+ boolean defaultFS ) {
305+ try {
306+ if (acc != null ) {
307+ PrivilegedExceptionAction <InputStream > pa = defaultFS
308+ ? () -> new FileInputStream (path .toFile ())
309+ : () -> Files .newInputStream (path );
310+ return perm != null
311+ ? AccessController .doPrivileged (pa , acc , perm )
312+ : AccessController .doPrivileged (pa , acc );
313+ } else {
314+ return defaultFS
315+ ? new FileInputStream (path .toFile ())
316+ : Files .newInputStream (path );
317+ }
318+ } catch (PrivilegedActionException pae ) {
319+ throw toUncheckedException (pae .getCause ());
320+ } catch (IOException io ) {
321+ throw new UncheckedIOException (io );
322+ }
261323 }
262324
263- private FilePublisher (Path name , FilePermission filePermission ) {
264- assert filePermission != null ? filePermission .getActions ().equals ("read" ) : true ;
265- file = name .toFile ();
266- this .filePermissions = filePermission == null ? EMPTY_FILE_PERMISSIONS
267- : new FilePermission [] { filePermission };
325+ private static RuntimeException toUncheckedException (Throwable t ) {
326+ if (t instanceof RuntimeException )
327+ throw (RuntimeException ) t ;
328+ if (t instanceof Error )
329+ throw (Error ) t ;
330+ if (t instanceof IOException )
331+ throw new UncheckedIOException ((IOException ) t );
332+ throw new UndeclaredThrowableException (t );
333+ }
334+
335+ private FilePublisher (Path name ,
336+ long length ,
337+ Function <Path , InputStream > inputStreamSupplier ) {
338+ path = name ;
339+ this .length = length ;
340+ this .inputStreamSupplier = inputStreamSupplier ;
268341 }
269342
270343 @ Override
271344 public void subscribe (Flow .Subscriber <? super ByteBuffer > subscriber ) {
272345 InputStream is = null ;
273346 Throwable t = null ;
274- if (System .getSecurityManager () == null ) {
275- try {
276- is = new FileInputStream (file );
277- } catch (IOException ioe ) {
278- t = ioe ;
279- }
280- } else {
281- try {
282- PrivilegedExceptionAction <FileInputStream > pa =
283- () -> new FileInputStream (file );
284- is = AccessController .doPrivileged (pa , null , filePermissions );
285- } catch (PrivilegedActionException pae ) {
286- t = pae .getCause ();
287- }
347+ try {
348+ is = inputStreamSupplier .apply (path );
349+ } catch (UncheckedIOException | UndeclaredThrowableException ue ) {
350+ t = ue .getCause ();
351+ } catch (Throwable th ) {
352+ t = th ;
288353 }
289354 final InputStream fis = is ;
290355 PullPublisher <ByteBuffer > publisher ;
@@ -298,12 +363,7 @@ public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
298363
299364 @ Override
300365 public long contentLength () {
301- if (System .getSecurityManager () == null ) {
302- return file .length ();
303- } else {
304- PrivilegedAction <Long > pa = () -> file .length ();
305- return AccessController .doPrivileged (pa , null , filePermissions );
306- }
366+ return length ;
307367 }
308368 }
309369
@@ -313,6 +373,7 @@ public long contentLength() {
313373 public static class StreamIterator implements Iterator <ByteBuffer > {
314374 final InputStream is ;
315375 final Supplier <? extends ByteBuffer > bufSupplier ;
376+ private volatile boolean eof ;
316377 volatile ByteBuffer nextBuffer ;
317378 volatile boolean need2Read = true ;
318379 volatile boolean haveNext ;
@@ -331,6 +392,8 @@ public static class StreamIterator implements Iterator<ByteBuffer> {
331392// }
332393
333394 private int read () {
395+ if (eof )
396+ return -1 ;
334397 nextBuffer = bufSupplier .get ();
335398 nextBuffer .clear ();
336399 byte [] buf = nextBuffer .array ();
@@ -339,6 +402,7 @@ private int read() {
339402 try {
340403 int n = is .read (buf , offset , cap );
341404 if (n == -1 ) {
405+ eof = true ;
342406 is .close ();
343407 return -1 ;
344408 }
0 commit comments