25 |
25 |
26 package jdk.internal.net.http; |
26 package jdk.internal.net.http; |
27 |
27 |
28 import java.io.File; |
28 import java.io.File; |
29 import java.io.FileInputStream; |
29 import java.io.FileInputStream; |
|
30 import java.io.FileNotFoundException; |
|
31 import java.io.FilePermission; |
30 import java.io.IOException; |
32 import java.io.IOException; |
31 import java.io.InputStream; |
33 import java.io.InputStream; |
32 import java.io.UncheckedIOException; |
34 import java.io.UncheckedIOException; |
33 import java.nio.ByteBuffer; |
35 import java.nio.ByteBuffer; |
34 import java.nio.charset.Charset; |
36 import java.nio.charset.Charset; |
|
37 import java.nio.file.Files; |
35 import java.nio.file.Path; |
38 import java.nio.file.Path; |
36 import java.security.AccessControlContext; |
39 import java.security.AccessControlContext; |
37 import java.security.AccessController; |
40 import java.security.AccessController; |
38 import java.security.PrivilegedAction; |
41 import java.security.PrivilegedAction; |
39 import java.security.PrivilegedActionException; |
42 import java.security.PrivilegedActionException; |
215 public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { |
218 public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { |
216 delegate.subscribe(subscriber); |
219 delegate.subscribe(subscriber); |
217 } |
220 } |
218 } |
221 } |
219 |
222 |
|
223 /** |
|
224 * Publishes the content of a given file. |
|
225 * |
|
226 * Privileged actions are performed within a limited doPrivileged that only |
|
227 * asserts the specific, read, file permission that was checked during the |
|
228 * construction of this FilePublisher. |
|
229 */ |
220 public static class FilePublisher implements BodyPublisher { |
230 public static class FilePublisher implements BodyPublisher { |
|
231 |
|
232 private static final FilePermission[] EMPTY_FILE_PERMISSIONS = new FilePermission[0]; |
|
233 |
221 private final File file; |
234 private final File file; |
222 |
235 private final FilePermission[] filePermissions; |
223 public FilePublisher(Path name) { |
236 |
|
237 private static String pathForSecurityCheck(Path path) { |
|
238 return path.toFile().getPath(); |
|
239 } |
|
240 |
|
241 /** |
|
242 * Factory for creating FilePublisher. |
|
243 * |
|
244 * Permission checks are performed here before construction of the |
|
245 * FilePublisher. Permission checking and construction are deliberately |
|
246 * and tightly co-located. |
|
247 */ |
|
248 public static FilePublisher create(Path path) throws FileNotFoundException { |
|
249 FilePermission filePermission = null; |
|
250 SecurityManager sm = System.getSecurityManager(); |
|
251 if (sm != null) { |
|
252 String fn = pathForSecurityCheck(path); |
|
253 FilePermission readPermission = new FilePermission(fn, "read"); |
|
254 sm.checkPermission(readPermission); |
|
255 filePermission = readPermission; |
|
256 } |
|
257 |
|
258 // existence check must be after permission checks |
|
259 if (Files.notExists(path)) |
|
260 throw new FileNotFoundException(path + " not found"); |
|
261 |
|
262 return new FilePublisher(path, filePermission); |
|
263 } |
|
264 |
|
265 private FilePublisher(Path name, FilePermission filePermission) { |
|
266 assert filePermission != null ? filePermission.getActions().equals("read") : true; |
224 file = name.toFile(); |
267 file = name.toFile(); |
|
268 this.filePermissions = filePermission == null ? EMPTY_FILE_PERMISSIONS |
|
269 : new FilePermission[] { filePermission }; |
225 } |
270 } |
226 |
271 |
227 @Override |
272 @Override |
228 public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { |
273 public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { |
229 InputStream is; |
274 InputStream is; |
230 try { |
275 if (System.getSecurityManager() == null) { |
231 PrivilegedExceptionAction<FileInputStream> pa = |
276 try { |
232 () -> new FileInputStream(file); |
277 is = new FileInputStream(file); |
233 is = AccessController.doPrivileged(pa); |
278 } catch (IOException ioe) { |
234 } catch (PrivilegedActionException pae) { |
279 throw new UncheckedIOException(ioe); |
235 throw new UncheckedIOException((IOException)pae.getCause()); |
280 } |
|
281 } else { |
|
282 try { |
|
283 PrivilegedExceptionAction<FileInputStream> pa = |
|
284 () -> new FileInputStream(file); |
|
285 is = AccessController.doPrivileged(pa, null, filePermissions); |
|
286 } catch (PrivilegedActionException pae) { |
|
287 throw new UncheckedIOException((IOException) pae.getCause()); |
|
288 } |
236 } |
289 } |
237 PullPublisher<ByteBuffer> publisher = |
290 PullPublisher<ByteBuffer> publisher = |
238 new PullPublisher<>(() -> new StreamIterator(is)); |
291 new PullPublisher<>(() -> new StreamIterator(is)); |
239 publisher.subscribe(subscriber); |
292 publisher.subscribe(subscriber); |
240 } |
293 } |
241 |
294 |
242 @Override |
295 @Override |
243 public long contentLength() { |
296 public long contentLength() { |
244 PrivilegedAction<Long> pa = () -> file.length(); |
297 if (System.getSecurityManager() == null) { |
245 return AccessController.doPrivileged(pa); |
298 return file.length(); |
|
299 } else { |
|
300 PrivilegedAction<Long> pa = () -> file.length(); |
|
301 return AccessController.doPrivileged(pa, null, filePermissions); |
|
302 } |
246 } |
303 } |
247 } |
304 } |
248 |
305 |
249 /** |
306 /** |
250 * Reads one buffer ahead all the time, blocking in hasNext() |
307 * Reads one buffer ahead all the time, blocking in hasNext() |