Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8245770

HttpResponse.BodySubscriber::ofFile throws UOE with non-default file systems

XMLWordPrintable

    • behavioral
    • minimal
    • Small change to previously unspecified exceptions being thrown, and a minor rework of non-default file system paths
    • Java API
    • SE

      Summary

      Specify a file system related constraint in the existing @throws declarations and the method-level documentation of the following methods:

      • HttpResponse.BodyHandler::ofFile
      • HttpResponse.BodySubscriber::ofFile
      • HttpResponse.BodyHandler::ofFileDownload

      Problem

      The original specifications of BodyHandler::ofFile, BodySubscriber::ofFile and BodyHandler::ofFileDownload assumed that the given path is associated with the default file system provider or behaves as such, and would fail if this was not the case.

      For BodyHandler::ofFile and BodySubscriber::ofFile, a fix is proposed to work with other file system path implementations when a security manager is enabled. Accordingly, specification changes should clarify in which case permissions checks are performed and when a SecurityException is thrown, depending on the file system of the given path.

      For BodyHandler::ofFileDownoad, the previous behaviour for a path of a non-default file system was inconsistent: With security manager enabled IOException was thrown, otherwise UnsupportedOperationException was thrown. This behaviour was not intended nor documented. A fix is proposed to fail consistently if the given path is not associated with the default file system provider. Specification changes should clarify that a IllegalArgumentException is thrown in this case.

      Solution

      BodyHandler::ofFile and BodySubscriber::ofFile: Specify the case in which permission checks are performed and the conditions of when a SecurityException is thrown, which is only in the case of the default file system provider.

      BodyHandler::ofFileDownload: The proposed change specifies that IllegalArgumentException is thrown in all cases if the provided path is not of the default file system.

      Specification

      java.net.http.HttpResponse.BodyHandlers

               /**
                * Returns a {@code BodyHandler<Path>} that returns a
                * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from
                * {@link BodySubscribers#ofFile(Path, OpenOption...)
                * BodySubscribers.ofFile(Path,OpenOption...)}.
                *
                * <p> When the {@code HttpResponse} object is returned, the body has
                * been completely written to the file, and {@link #body()} returns a
                * reference to its {@link Path}.
                *
      -         * <p> Security manager permission checks are performed in this factory
      -         * method, when the {@code BodyHandler} is created. Care must be taken
      -         * that the {@code BodyHandler} is not shared with untrusted code.
      +         * <p> In the case of the default file system provider, security manager
      +         * permission checks are performed in this factory method, when the
      +         * {@code BodyHandler} is created. Otherwise,
      +         * {@linkplain FileChannel#open(Path, OpenOption...) permission checks}
      +         * may be performed asynchronously against the caller's context
      +         * at file access time.
      +         * Care must be taken that the {@code BodyHandler} is not shared with
      +         * untrusted code.
                *
                * @param  file the file to store the body in
                * @param  openOptions any options to use when opening/creating the file
                * @return a response body handler
                * @throws IllegalArgumentException if an invalid set of open options
      -         *          are specified
      -         * @throws SecurityException If a security manager has been installed
      -         *          and it denies {@linkplain SecurityManager#checkWrite(String)
      -         *          write access} to the file.
      +         *         are specified
      +         * @throws SecurityException in the case of the default file system
      +         *         provider, and a security manager is installed,
      +         *         {@link SecurityManager#checkWrite(String) checkWrite}
      +         *         is invoked to check write access to the given file
                */
               public static BodyHandler<Path> ofFile(Path file, OpenOption... openOptions) {

      (...)

               /**
                * Returns a {@code BodyHandler<Path>} that returns a
                * {@link BodySubscriber BodySubscriber}{@code <Path>}.
                *
                * <p> Equivalent to: {@code ofFile(file, CREATE, WRITE)}
                *
      -         * <p> Security manager permission checks are performed in this factory
      -         * method, when the {@code BodyHandler} is created. Care must be taken
      -         * that the {@code BodyHandler} is not shared with untrusted code.
      +         * <p> In the case of the default file system provider, security manager
      +         * permission checks are performed in this factory method, when the
      +         * {@code BodyHandler} is created. Otherwise,
      +         * {@linkplain FileChannel#open(Path, OpenOption...) permission checks}
      +         * may be performed asynchronously against the caller's context
      +         * at file access time.
      +         * Care must be taken that the {@code BodyHandler} is not shared with
      +         * untrusted code.
                *
                * @param  file the file to store the body in
                * @return a response body handler
      -         * @throws SecurityException If a security manager has been installed
      -         *          and it denies {@linkplain SecurityManager#checkWrite(String)
      -         *          write access} to the file.
      +         * @throws SecurityException in the case of the default file system
      +         *         provider, and a security manager is installed,
      +         *         {@link SecurityManager#checkWrite(String) checkWrite}
      +         *         is invoked to check write access to the given file
                */
               public static BodyHandler<Path> ofFile(Path file) {

      (...)

               /**
                * Returns a {@code BodyHandler<Path>} that returns a
                * {@link BodySubscriber BodySubscriber}&lt;{@link Path}&gt;
                * where the download directory is specified, but the filename is
                * obtained from the {@code Content-Disposition} response header. The
                * {@code Content-Disposition} header must specify the <i>attachment</i>
                * type and must also contain a <i>filename</i> parameter. If the
                * filename specifies multiple path components only the final component
                * is used as the filename (with the given directory name).
                *
                * <p> When the {@code HttpResponse} object is returned, the body has
                * been completely written to the file and {@link #body()} returns a
                * {@code Path} object for the file. The returned {@code Path} is the
                * combination of the supplied directory name and the file name supplied
                * by the server. If the destination directory does not exist or cannot
                * be written to, then the response will fail with an {@link IOException}.
                *
                * <p> Security manager permission checks are performed in this factory
                * method, when the {@code BodyHandler} is created. Care must be taken
                * that the {@code BodyHandler} is not shared with untrusted code.
                *
                * @param  directory the directory to store the file in
                * @param  openOptions open options used when opening the file
                * @return a response body handler
                * @throws IllegalArgumentException if the given path does not exist,
      -         *          is not a directory, is not writable, or if an invalid set
      -         *          of open options are specified
      +         *         is not of the default file system, is not a directory,
      +         *         is not writable, or if an invalid set of open options
      +         *         are specified
      -         * @throws SecurityException If a security manager has been installed
      +         * @throws SecurityException in the case of the default file system
      +         *         provider and a security manager has been installed,
                *         and it denies
                *         {@linkplain SecurityManager#checkRead(String) read access}
                *         to the directory, or it denies
                *         {@linkplain SecurityManager#checkWrite(String) write access}
                *         to the directory, or it denies
                *         {@linkplain SecurityManager#checkWrite(String) write access}
                *         to the files within the directory.
                */
               public static BodyHandler<Path> ofFileDownload(Path directory, OpenOption... openOptions) {

      java.net.http.HttpResponse.BodySubscribers

               /**
                * Returns a {@code BodySubscriber} which stores the response body in a
                * file opened with the given options and name. The file will be opened
                * with the given options using {@link FileChannel#open(Path,OpenOption...)
                * FileChannel.open} just before the body is read. Any exception thrown
                * will be returned or thrown from {@link HttpClient#send(HttpRequest,
                * BodyHandler) HttpClient::send} or {@link HttpClient#sendAsync(HttpRequest,
                * BodyHandler) HttpClient::sendAsync} as appropriate.
                *
                * <p> The {@link HttpResponse} using this subscriber is available after
                * the entire response has been read.
                *
      -         * <p> Security manager permission checks are performed in this factory
      -         * method, when the {@code BodySubscriber} is created. Care must be taken
      -         * that the {@code BodyHandler} is not shared with untrusted code.
      +         * <p> In the case of the default file system provider, security manager
      +         * permission checks are performed in this factory method, when the
      +         * {@code BodySubscriber} is created. Otherwise,
      +         * {@linkplain FileChannel#open(Path, OpenOption...) permission checks}
      +         * may be performed asynchronously against the caller's context
      +         * at file access time.
      +         * Care must be taken that the {@code BodySubscriber} is not shared with
      +         * untrusted code.
                *
                * @param  file the file to store the body in
                * @param  openOptions the list of options to open the file with
                * @return a body subscriber
                * @throws IllegalArgumentException if an invalid set of open options
      -         *          are specified
      -         * @throws SecurityException if a security manager has been installed
      -         *          and it denies {@linkplain SecurityManager#checkWrite(String)
      -         *          write access} to the file
      +         *         are specified
      +         * @throws SecurityException in the case of the default file system
      +         *         provider, and a security manager is installed,
      +         *         {@link SecurityManager#checkWrite(String) checkWrite}
      +         *         is invoked to check write access to the given file
                */
               public static BodySubscriber<Path> ofFile(Path file, OpenOption... openOptions) {

      (...)

               /**
                * Returns a {@code BodySubscriber} which stores the response body in a
                * file opened with the given name.
                *
                * <p> Equivalent to: {@code ofFile(file, CREATE, WRITE)}
                *
      -         * <p> Security manager permission checks are performed in this factory
      -         * method, when the {@code BodySubscriber} is created. Care must be taken
      -         * that the {@code BodyHandler} is not shared with untrusted code.
      +         * <p> In the case of the default file system provider, security manager
      +         * permission checks are performed in this factory method, when the
      +         * {@code BodySubscriber} is created. Otherwise,
      +         * {@linkplain FileChannel#open(Path, OpenOption...) permission checks}
      +         * may be performed asynchronously against the caller's context
      +         * at file access time.
      +         * Care must be taken that the {@code BodySubscriber} is not shared with
      +         * untrusted code.
                *
                * @param  file the file to store the body in
                * @return a body subscriber
      -         * @throws SecurityException if a security manager has been installed
      -         *          and it denies {@linkplain SecurityManager#checkWrite(String)
      -         *          write access} to the file
      +         * @throws SecurityException in the case of the default file system
      +         *         provider, and a security manager is installed,
      +         *         {@link SecurityManager#checkWrite(String) checkWrite}
      +         *         is invoked to check write access to the given file
                */
               public static BodySubscriber<Path> ofFile(Path file) {

            jboes Julia Boes (Inactive)
            jboes Julia Boes (Inactive)
            Chris Hegarty, Daniel Fuchs
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: