src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java
branchhttp-client-branch
changeset 56257 82a9340bdda6
parent 56138 4f92b988600e
child 56410 1b37529eaf3a
equal deleted inserted replaced
56256:0fe17c3f9b4f 56257:82a9340bdda6
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package jdk.internal.net.http;
    26 package jdk.internal.net.http;
    27 
    27 
       
    28 import java.io.File;
       
    29 import java.io.FilePermission;
    28 import java.io.IOException;
    30 import java.io.IOException;
    29 import java.io.UncheckedIOException;
    31 import java.io.UncheckedIOException;
    30 import java.net.URI;
    32 import java.net.URI;
       
    33 import java.nio.file.Files;
    31 import java.nio.file.OpenOption;
    34 import java.nio.file.OpenOption;
    32 import java.nio.file.Path;
    35 import java.nio.file.Path;
    33 import java.nio.file.Paths;
    36 import java.nio.file.Paths;
    34 import java.nio.file.StandardOpenOption;
       
    35 import java.util.List;
    37 import java.util.List;
    36 import java.util.concurrent.CompletableFuture;
    38 import java.util.concurrent.CompletableFuture;
    37 import java.util.concurrent.ConcurrentMap;
    39 import java.util.concurrent.ConcurrentMap;
    38 import java.util.function.Function;
    40 import java.util.function.Function;
    39 import java.net.http.HttpHeaders;
    41 import java.net.http.HttpHeaders;
    48 
    50 
    49 public final class ResponseBodyHandlers {
    51 public final class ResponseBodyHandlers {
    50 
    52 
    51     private ResponseBodyHandlers() { }
    53     private ResponseBodyHandlers() { }
    52 
    54 
       
    55     private static final String pathForSecurityCheck(Path path) {
       
    56         return path.toFile().getPath();
       
    57     }
       
    58 
    53     /**
    59     /**
    54      * A Path body handler.
    60      * A Path body handler.
    55      */
    61      */
    56     public static class PathBodyHandler implements BodyHandler<Path>{
    62     public static class PathBodyHandler implements BodyHandler<Path>{
    57         private final Path file;
    63         private final Path file;
    58         private final List<OpenOption> openOptions;
    64         private final List<OpenOption> openOptions;  // immutable list
    59 
    65         private final FilePermission filePermission;
    60         public PathBodyHandler(Path file, List<OpenOption> openOptions) {
    66 
       
    67         /**
       
    68          * Factory for creating PathBodyHandler.
       
    69          *
       
    70          * Permission checks are performed here before construction of the
       
    71          * PathBodyHandler. Permission checking and construction are
       
    72          * deliberately and tightly co-located.
       
    73          */
       
    74         public static PathBodyHandler create(Path file,
       
    75                                              List<OpenOption> openOptions) {
       
    76             FilePermission filePermission = null;
       
    77             SecurityManager sm = System.getSecurityManager();
       
    78             if (sm != null) {
       
    79                 String fn = pathForSecurityCheck(file);
       
    80                 FilePermission writePermission = new FilePermission(fn, "write");
       
    81                 sm.checkPermission(writePermission);
       
    82                 filePermission = writePermission;
       
    83             }
       
    84             return new PathBodyHandler(file, openOptions, filePermission);
       
    85         }
       
    86 
       
    87         private PathBodyHandler(Path file,
       
    88                                 List<OpenOption> openOptions,
       
    89                                 FilePermission filePermission) {
    61             this.file = file;
    90             this.file = file;
    62             this.openOptions = openOptions;
    91             this.openOptions = openOptions;
       
    92             this.filePermission = filePermission;
    63         }
    93         }
    64 
    94 
    65         @Override
    95         @Override
    66         public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) {
    96         public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) {
    67             return new PathSubscriber(file, openOptions);
    97             return new PathSubscriber(file, openOptions, filePermission);
    68         }
    98         }
    69     }
    99     }
    70 
   100 
    71     /** With push promise Map implementation */
   101     /** With push promise Map implementation */
    72     public static class PushPromisesHandlerWithMap<T>
   102     public static class PushPromisesHandlerWithMap<T>
   116 
   146 
   117     // Similar to Path body handler, but for file download.
   147     // Similar to Path body handler, but for file download.
   118     public static class FileDownloadBodyHandler implements BodyHandler<Path> {
   148     public static class FileDownloadBodyHandler implements BodyHandler<Path> {
   119         private final Path directory;
   149         private final Path directory;
   120         private final List<OpenOption> openOptions;
   150         private final List<OpenOption> openOptions;
   121 
   151         private final FilePermission[] filePermissions;  // may be null
   122         public FileDownloadBodyHandler(Path directory, List<OpenOption> openOptions) {
   152 
       
   153         /**
       
   154          * Factory for creating FileDownloadBodyHandler.
       
   155          *
       
   156          * Permission checks are performed here before construction of the
       
   157          * FileDownloadBodyHandler. Permission checking and construction are
       
   158          * deliberately and tightly co-located.
       
   159          */
       
   160         public static FileDownloadBodyHandler create(Path directory,
       
   161                                                      List<OpenOption> openOptions) {
       
   162             FilePermission filePermissions[] = null;
       
   163             SecurityManager sm = System.getSecurityManager();
       
   164             if (sm != null) {
       
   165                 String fn = pathForSecurityCheck(directory);
       
   166                 FilePermission writePermission = new FilePermission(fn, "write");
       
   167                 String writePathPerm = fn + File.separatorChar + "*";
       
   168                 FilePermission writeInDirPermission = new FilePermission(writePathPerm, "write");
       
   169                 sm.checkPermission(writeInDirPermission);
       
   170                 FilePermission readPermission = new FilePermission(fn, "read");
       
   171                 sm.checkPermission(readPermission);
       
   172 
       
   173                 // read permission is only needed before determine the below checks
       
   174                 // only write permission is required when downloading to the file
       
   175                 filePermissions = new FilePermission[] { writePermission, writeInDirPermission };
       
   176             }
       
   177 
       
   178             // existence, etc, checks must be after permission checks
       
   179             if (Files.notExists(directory))
       
   180                 throw new IllegalArgumentException("non-existent directory: " + directory);
       
   181             if (!Files.isDirectory(directory))
       
   182                 throw new IllegalArgumentException("not a directory: " + directory);
       
   183             if (!Files.isWritable(directory))
       
   184                 throw new IllegalArgumentException("non-writable directory: " + directory);
       
   185 
       
   186             return new FileDownloadBodyHandler(directory, openOptions, filePermissions);
       
   187 
       
   188         }
       
   189 
       
   190         private FileDownloadBodyHandler(Path directory,
       
   191                                        List<OpenOption> openOptions,
       
   192                                        FilePermission... filePermissions) {
   123             this.directory = directory;
   193             this.directory = directory;
   124             this.openOptions = openOptions;
   194             this.openOptions = openOptions;
       
   195             this.filePermissions = filePermissions;
   125         }
   196         }
   126 
   197 
   127         /** The "attachment" disposition-type and separator. */
   198         /** The "attachment" disposition-type and separator. */
   128         static final String DISPOSITION_TYPE = "attachment;";
   199         static final String DISPOSITION_TYPE = "attachment;";
   129 
   200 
   202             if (!file.startsWith(directory)) {
   273             if (!file.startsWith(directory)) {
   203                 throw unchecked(statusCode, headers,
   274                 throw unchecked(statusCode, headers,
   204                         "Resulting file, " + file.toString() + ", outside of given directory");
   275                         "Resulting file, " + file.toString() + ", outside of given directory");
   205             }
   276             }
   206 
   277 
   207             return new PathSubscriber(file, openOptions);
   278             return new PathSubscriber(file, openOptions, filePermissions);
   208         }
   279         }
   209     }
   280     }
   210 }
   281 }