< prev index next >

src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java

Print this page




 207      * In the second example, the function returns a different processor depending
 208      * on the status code.
 209      * <pre>
 210      * {@code
 211      *      HttpResponse<Path> resp1 = HttpRequest
 212      *              .create(URI.create("http://www.foo.com"))
 213      *              .GET()
 214      *              .response(
 215      *                  (status, headers) -> status == 200
 216      *                      ? BodyProcessor.asFile(Paths.get("/tmp/f"))
 217      *                      : BodyProcessor.discard(Paths.get("/NULL")));
 218      * }
 219      * </pre>
 220      *
 221      * @param <T> the response body type.
 222      */
 223     @FunctionalInterface
 224     public interface BodyHandler<T> {
 225 
 226         /**
 227          * Return a {@link BodyProcessor BodyProcessor} considering the given response status
 228          * code and headers. This method is always called before the body is read
 229          * and its implementation can decide to keep the body and store it somewhere
 230          * or else discard it, by  returning the {@code BodyProcessor} returned
 231          * from {@link BodyProcessor#discard(java.lang.Object) discard()}.
 232          *
 233          * @param statusCode the HTTP status code received
 234          * @param responseHeaders the response headers received
 235          * @return
 236          */
 237         public BodyProcessor<T> apply(int statusCode, HttpHeaders responseHeaders);
 238 
 239         /**
 240          * Returns a response body handler which discards the response body and
 241          * uses the given value as a replacement for it.
 242          *
 243          * @param <U> the response body type
 244          * @param value the value of U to return as the body
 245          * @return
 246          */
 247         public static <U> BodyHandler<U> discard(U value) {
 248             return (status, headers) -> BodyProcessor.discard(value);
 249         }
 250 
 251         /**
 252          * Returns a {@code BodyHandler<String>} that returns a
 253          * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from
 254          * {@link BodyProcessor#asString(java.nio.charset.Charset)
 255          * BodyProcessor.asString(Charset)}. If a charset is provided, the
 256          * body is decoded using it. If charset is {@code null} then the processor
 257          * tries to determine the character set from the {@code Content-encoding}
 258          * header. If that charset is not supported then
 259          * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used.
 260          *
 261          * @param charset the name of the charset to interpret the body as. If
 262          * {@code null} then charset determined from Content-encoding header
 263          * @return a response handler
 264          */
 265         public static BodyHandler<String> asString(Charset charset) {
 266             return (status, headers) -> {
 267                 if (charset != null) {
 268                     return BodyProcessor.asString(charset);
 269                 }
 270                 return BodyProcessor.asString(charsetFrom(headers));
 271             };
 272         }
 273 
 274 
 275         /**
 276          * Returns a {@code BodyHandler<Path>} that returns a
 277          * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from
 278          * {@link BodyProcessor#asFile(Path) BodyProcessor.asFile(Path)}.
 279          * <p>
 280          * When the {@code HttpResponse} object is returned, the body has been completely
 281          * written to the file, and {@link #body()} returns a reference to its
 282          * {@link Path}.
 283          *
 284          * @param file the file to store the body in
 285          * @return a response handler
 286          */
 287         public static BodyHandler<Path> asFile(Path file) {
 288             return (status, headers) -> BodyProcessor.asFile(file);
 289         }
 290 
 291         /**
 292          * Returns a {@code BodyHandler<Path>} that returns a
 293          * {@link BodyProcessor BodyProcessor}&lt;{@link Path}&gt;
 294          * where the download directory is specified, but the filename is
 295          * obtained from the {@code Content-Disposition} response header. The
 296          * {@code Content-Disposition} header must specify the <i>attachment</i> type
 297          * and must also contain a
 298          * <i>filename</i> parameter. If the filename specifies multiple path
 299          * components only the final component is used as the filename (with the
 300          * given directory name). When the {@code HttpResponse} object is
 301          * returned, the body has been completely written to the file and {@link
 302          * #body()} returns a {@code Path} object for the file. The returned {@code Path} is the
 303          * combination of the supplied directory name and the file name supplied
 304          * by the server. If the destination directory does not exist or cannot
 305          * be written to, then the response will fail with an {@link IOException}.
 306          *
 307          * @param directory the directory to store the file in
 308          * @param openOptions open options
 309          * @return a response handler
 310          */
 311         public static BodyHandler<Path> asFileDownload(Path directory, OpenOption... openOptions) {
 312             return (status, headers) -> {
 313                 String dispoHeader = headers.firstValue("Content-Disposition")
 314                         .orElseThrow(() -> unchecked(new IOException("No Content-Disposition")));
 315                 if (!dispoHeader.startsWith("attachment;")) {
 316                     throw unchecked(new IOException("Unknown Content-Disposition type"));
 317                 }
 318                 int n = dispoHeader.indexOf("filename=");
 319                 if (n == -1) {
 320                     throw unchecked(new IOException("Bad Content-Disposition type"));
 321                 }
 322                 int lastsemi = dispoHeader.lastIndexOf(';');
 323                 String disposition;
 324                 if (lastsemi < n) {
 325                     disposition = dispoHeader.substring(n + 9);
 326                 } else {
 327                     disposition = dispoHeader.substring(n + 9, lastsemi);
 328                 }
 329                 Path file = Paths.get(directory.toString(), disposition);
 330                 return BodyProcessor.asFile(file, openOptions);
 331             };
 332         }
 333 
 334         /**
 335          * Returns a {@code BodyHandler<Path>} that returns a
 336          * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from
 337          * {@link BodyProcessor#asFile(java.nio.file.Path, java.nio.file.OpenOption...)
 338          * BodyProcessor.asFile(Path,OpenOption...)}.
 339          * <p>
 340          * When the {@code HttpResponse} object is returned, the body has been completely
 341          * written to the file, and {@link #body()} returns a reference to its
 342          * {@link Path}.
 343          *
 344          * @param file the filename to store the body in
 345          * @param openOptions any options to use when opening/creating the file
 346          * @return a response handler
 347          */
 348         public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) {
 349             return (status, headers) -> BodyProcessor.asFile(file, openOptions);
 350         }
 351 
 352         /**
 353          * Returns a {@code BodyHandler<Void>} that returns a
 354          * {@link BodyProcessor BodyProcessor}{@code <Void>} obtained from
 355          * {@link BodyProcessor#asByteArrayConsumer(java.util.function.Consumer)
 356          * BodyProcessor.asByteArrayConsumer(Consumer)}.
 357          * <p>
 358          * When the {@code HttpResponse} object is returned, the body has been completely
 359          * written to the consumer.
 360          *
 361          * @param consumer a Consumer to accept the response body
 362          * @return a a response handler
 363          */
 364         public static BodyHandler<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) {
 365             return (status, headers) -> BodyProcessor.asByteArrayConsumer(consumer);
 366         }
 367 
 368         /**
 369          * Returns a {@code BodyHandler<byte[]>} that returns a
 370          * {@link BodyProcessor BodyProcessor}&lt;{@code byte[]}&gt; obtained
 371          * from {@link BodyProcessor#asByteArray() BodyProcessor.asByteArray()}.
 372          * <p>
 373          * When the {@code HttpResponse} object is returned, the body has been completely
 374          * written to the byte array.
 375          *
 376          * @return a response handler
 377          */
 378         public static BodyHandler<byte[]> asByteArray() {
 379             return (status, headers) -> BodyProcessor.asByteArray();
 380         }
 381 
 382         /**
 383          * Returns a {@code BodyHandler<String>} that returns a
 384          * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from
 385          * {@link BodyProcessor#asString(java.nio.charset.Charset)
 386          * BodyProcessor.asString(Charset)}. The body is
 387          * decoded using the character set specified in
 388          * the {@code Content-encoding} response header. If there is no such
 389          * header, or the character set is not supported, then
 390          * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used.
 391          * <p>
 392          * When the {@code HttpResponse} object is returned, the body has been completely
 393          * written to the string.
 394          *
 395          * @return a response handler
 396          */
 397         public static BodyHandler<String> asString() {
 398             return (status, headers) -> BodyProcessor.asString(charsetFrom(headers));
 399         }
 400     }
 401 
 402     /**
 403      * A processor for response bodies.
 404      * {@Incubating}
 405      * <p>
 406      * The object acts as a {@link Flow.Subscriber}&lt;{@link ByteBuffer}&gt; to
 407      * the HTTP client implementation which publishes ByteBuffers containing the
 408      * response body. The processor converts the incoming buffers of data to
 409      * some user-defined object type {@code T}.
 410      * <p>
 411      * The {@link #getBody()} method returns a {@link CompletionStage}{@code <T>}
 412      * that provides the response body object. The {@code CompletionStage} must
 413      * be obtainable at any time. When it completes depends on the nature
 414      * of type {@code T}. In many cases, when {@code T} represents the entire body after being
 415      * read then it completes after the body has been read. If {@code T} is a streaming


 589          *
 590          * [Note] The reason for switching to this callback interface rather
 591          * than using CompletableFutures supplied to onRequest() is that there
 592          * is a subtle interaction between those CFs and the CF returned from
 593          * completion() (or when onComplete() was called formerly). The completion()
 594          * CF will not complete until after all of the work done by the onResponse()
 595          * calls is done. Whereas if you just create CF's dependent on a supplied
 596          * CF (to onRequest()) then the implementation has no visibility of the
 597          * dependent CFs and can't guarantee to call onComplete() (or complete
 598          * the completion() CF) after the dependent CFs complete.
 599          *
 600          * @param response the response received
 601          */
 602         void onResponse(HttpResponse<T> response);
 603 
 604         /**
 605          * Called if an error occurs receiving a response. For each request
 606          * either one of onResponse() or onError() is guaranteed to be called,
 607          * but not both.
 608          *
 609          * @param request
 610          * @param t the Throwable that caused the error
 611          */
 612         void onError(HttpRequest request, Throwable t);
 613 
 614         /**
 615          * Returns a {@link java.util.concurrent.CompletableFuture}{@code <U>}
 616          * which completes when the aggregate result object itself is available.
 617          * It is expected that the returned {@code CompletableFuture} will depend
 618          * on one of the given {@code CompletableFuture<Void}s which themselves complete
 619          * after all individual responses associated with the multi response
 620          * have completed, or after all push promises have been received.
 621          * <p>
 622          * @implNote Implementations might follow the pattern shown below
 623          * <pre>
 624          * {@code
 625          *      CompletableFuture<U> completion(
 626          *              CompletableFuture<Void> onComplete,
 627          *              CompletableFuture<Void> onFinalPushPromise)
 628          *      {
 629          *          return onComplete.thenApply((v) -> {


 700          * {@code
 701          *          HttpRequest request = HttpRequest.newBuilder()
 702          *                  .uri(URI.create("https://www.foo.com/"))
 703          *                  .GET()
 704          *                  .build();
 705          *
 706          *          HttpClient client = HttpClient.newHttpClient();
 707          *
 708          *          Map<HttpRequest,CompletableFuture<HttpResponse<String>>> results = client
 709          *              .sendAsync(request, MultiProcessor.asMap(
 710          *                  (req) -> Optional.of(HttpResponse.BodyHandler.asString())))
 711          *              .join();
 712          * }</pre>
 713          * <p>
 714          * The lambda in this example is the simplest possible implementation,
 715          * where neither the incoming requests are examined, nor the response
 716          * headers, and every push that the server sends is accepted. When the
 717          * join() call returns, all {@code HttpResponse}s and their associated
 718          * body objects are available.
 719          *
 720          * @param <V>
 721          * @param pushHandler a function invoked for each request or push
 722          * promise
 723          * @return a MultiProcessor
 724          */
 725         public static <V> MultiProcessor<MultiMapResult<V>,V> asMap(
 726             Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler) {
 727 
 728             return asMap(pushHandler, true);
 729         }
 730 
 731     }
 732 }


 207      * In the second example, the function returns a different processor depending
 208      * on the status code.
 209      * <pre>
 210      * {@code
 211      *      HttpResponse<Path> resp1 = HttpRequest
 212      *              .create(URI.create("http://www.foo.com"))
 213      *              .GET()
 214      *              .response(
 215      *                  (status, headers) -> status == 200
 216      *                      ? BodyProcessor.asFile(Paths.get("/tmp/f"))
 217      *                      : BodyProcessor.discard(Paths.get("/NULL")));
 218      * }
 219      * </pre>
 220      *
 221      * @param <T> the response body type.
 222      */
 223     @FunctionalInterface
 224     public interface BodyHandler<T> {
 225 
 226         /**
 227          * Returns a {@link BodyProcessor BodyProcessor} considering the given response status
 228          * code and headers. This method is always called before the body is read
 229          * and its implementation can decide to keep the body and store it somewhere
 230          * or else discard it, by  returning the {@code BodyProcessor} returned
 231          * from {@link BodyProcessor#discard(java.lang.Object) discard()}.
 232          *
 233          * @param statusCode the HTTP status code received
 234          * @param responseHeaders the response headers received
 235          * @return a response body handler
 236          */
 237         public BodyProcessor<T> apply(int statusCode, HttpHeaders responseHeaders);
 238 
 239         /**
 240          * Returns a response body handler which discards the response body and
 241          * uses the given value as a replacement for it.
 242          *
 243          * @param <U> the response body type
 244          * @param value the value of U to return as the body
 245          * @return a response body handler
 246          */
 247         public static <U> BodyHandler<U> discard(U value) {
 248             return (status, headers) -> BodyProcessor.discard(value);
 249         }
 250 
 251         /**
 252          * Returns a {@code BodyHandler<String>} that returns a
 253          * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from
 254          * {@link BodyProcessor#asString(java.nio.charset.Charset)
 255          * BodyProcessor.asString(Charset)}. If a charset is provided, the
 256          * body is decoded using it. If charset is {@code null} then the processor
 257          * tries to determine the character set from the {@code Content-encoding}
 258          * header. If that charset is not supported then
 259          * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used.
 260          *
 261          * @param charset the name of the charset to interpret the body as. If
 262          * {@code null} then charset determined from Content-encoding header
 263          * @return a response body handler
 264          */
 265         public static BodyHandler<String> asString(Charset charset) {
 266             return (status, headers) -> {
 267                 if (charset != null) {
 268                     return BodyProcessor.asString(charset);
 269                 }
 270                 return BodyProcessor.asString(charsetFrom(headers));
 271             };
 272         }
 273 
 274 
 275         /**
 276          * Returns a {@code BodyHandler<Path>} that returns a
 277          * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from
 278          * {@link BodyProcessor#asFile(Path) BodyProcessor.asFile(Path)}.
 279          * <p>
 280          * When the {@code HttpResponse} object is returned, the body has been completely
 281          * written to the file, and {@link #body()} returns a reference to its
 282          * {@link Path}.
 283          *
 284          * @param file the file to store the body in
 285          * @return a response body handler
 286          */
 287         public static BodyHandler<Path> asFile(Path file) {
 288             return (status, headers) -> BodyProcessor.asFile(file);
 289         }
 290 
 291         /**
 292          * Returns a {@code BodyHandler<Path>} that returns a
 293          * {@link BodyProcessor BodyProcessor}&lt;{@link Path}&gt;
 294          * where the download directory is specified, but the filename is
 295          * obtained from the {@code Content-Disposition} response header. The
 296          * {@code Content-Disposition} header must specify the <i>attachment</i> type
 297          * and must also contain a
 298          * <i>filename</i> parameter. If the filename specifies multiple path
 299          * components only the final component is used as the filename (with the
 300          * given directory name). When the {@code HttpResponse} object is
 301          * returned, the body has been completely written to the file and {@link
 302          * #body()} returns a {@code Path} object for the file. The returned {@code Path} is the
 303          * combination of the supplied directory name and the file name supplied
 304          * by the server. If the destination directory does not exist or cannot
 305          * be written to, then the response will fail with an {@link IOException}.
 306          *
 307          * @param directory the directory to store the file in
 308          * @param openOptions open options
 309          * @return a response body handler
 310          */
 311         public static BodyHandler<Path> asFileDownload(Path directory, OpenOption... openOptions) {
 312             return (status, headers) -> {
 313                 String dispoHeader = headers.firstValue("Content-Disposition")
 314                         .orElseThrow(() -> unchecked(new IOException("No Content-Disposition")));
 315                 if (!dispoHeader.startsWith("attachment;")) {
 316                     throw unchecked(new IOException("Unknown Content-Disposition type"));
 317                 }
 318                 int n = dispoHeader.indexOf("filename=");
 319                 if (n == -1) {
 320                     throw unchecked(new IOException("Bad Content-Disposition type"));
 321                 }
 322                 int lastsemi = dispoHeader.lastIndexOf(';');
 323                 String disposition;
 324                 if (lastsemi < n) {
 325                     disposition = dispoHeader.substring(n + 9);
 326                 } else {
 327                     disposition = dispoHeader.substring(n + 9, lastsemi);
 328                 }
 329                 Path file = Paths.get(directory.toString(), disposition);
 330                 return BodyProcessor.asFile(file, openOptions);
 331             };
 332         }
 333 
 334         /**
 335          * Returns a {@code BodyHandler<Path>} that returns a
 336          * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from
 337          * {@link BodyProcessor#asFile(java.nio.file.Path, java.nio.file.OpenOption...)
 338          * BodyProcessor.asFile(Path,OpenOption...)}.
 339          * <p>
 340          * When the {@code HttpResponse} object is returned, the body has been completely
 341          * written to the file, and {@link #body()} returns a reference to its
 342          * {@link Path}.
 343          *
 344          * @param file the filename to store the body in
 345          * @param openOptions any options to use when opening/creating the file
 346          * @return a response body handler
 347          */
 348         public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) {
 349             return (status, headers) -> BodyProcessor.asFile(file, openOptions);
 350         }
 351 
 352         /**
 353          * Returns a {@code BodyHandler<Void>} that returns a
 354          * {@link BodyProcessor BodyProcessor}{@code <Void>} obtained from
 355          * {@link BodyProcessor#asByteArrayConsumer(java.util.function.Consumer)
 356          * BodyProcessor.asByteArrayConsumer(Consumer)}.
 357          * <p>
 358          * When the {@code HttpResponse} object is returned, the body has been completely
 359          * written to the consumer.
 360          *
 361          * @param consumer a Consumer to accept the response body
 362          * @return a response body handler
 363          */
 364         public static BodyHandler<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) {
 365             return (status, headers) -> BodyProcessor.asByteArrayConsumer(consumer);
 366         }
 367 
 368         /**
 369          * Returns a {@code BodyHandler<byte[]>} that returns a
 370          * {@link BodyProcessor BodyProcessor}&lt;{@code byte[]}&gt; obtained
 371          * from {@link BodyProcessor#asByteArray() BodyProcessor.asByteArray()}.
 372          * <p>
 373          * When the {@code HttpResponse} object is returned, the body has been completely
 374          * written to the byte array.
 375          *
 376          * @return a response body handler
 377          */
 378         public static BodyHandler<byte[]> asByteArray() {
 379             return (status, headers) -> BodyProcessor.asByteArray();
 380         }
 381 
 382         /**
 383          * Returns a {@code BodyHandler<String>} that returns a
 384          * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from
 385          * {@link BodyProcessor#asString(java.nio.charset.Charset)
 386          * BodyProcessor.asString(Charset)}. The body is
 387          * decoded using the character set specified in
 388          * the {@code Content-encoding} response header. If there is no such
 389          * header, or the character set is not supported, then
 390          * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used.
 391          * <p>
 392          * When the {@code HttpResponse} object is returned, the body has been completely
 393          * written to the string.
 394          *
 395          * @return a response body handler
 396          */
 397         public static BodyHandler<String> asString() {
 398             return (status, headers) -> BodyProcessor.asString(charsetFrom(headers));
 399         }
 400     }
 401 
 402     /**
 403      * A processor for response bodies.
 404      * {@Incubating}
 405      * <p>
 406      * The object acts as a {@link Flow.Subscriber}&lt;{@link ByteBuffer}&gt; to
 407      * the HTTP client implementation which publishes ByteBuffers containing the
 408      * response body. The processor converts the incoming buffers of data to
 409      * some user-defined object type {@code T}.
 410      * <p>
 411      * The {@link #getBody()} method returns a {@link CompletionStage}{@code <T>}
 412      * that provides the response body object. The {@code CompletionStage} must
 413      * be obtainable at any time. When it completes depends on the nature
 414      * of type {@code T}. In many cases, when {@code T} represents the entire body after being
 415      * read then it completes after the body has been read. If {@code T} is a streaming


 589          *
 590          * [Note] The reason for switching to this callback interface rather
 591          * than using CompletableFutures supplied to onRequest() is that there
 592          * is a subtle interaction between those CFs and the CF returned from
 593          * completion() (or when onComplete() was called formerly). The completion()
 594          * CF will not complete until after all of the work done by the onResponse()
 595          * calls is done. Whereas if you just create CF's dependent on a supplied
 596          * CF (to onRequest()) then the implementation has no visibility of the
 597          * dependent CFs and can't guarantee to call onComplete() (or complete
 598          * the completion() CF) after the dependent CFs complete.
 599          *
 600          * @param response the response received
 601          */
 602         void onResponse(HttpResponse<T> response);
 603 
 604         /**
 605          * Called if an error occurs receiving a response. For each request
 606          * either one of onResponse() or onError() is guaranteed to be called,
 607          * but not both.
 608          *
 609          * @param request the main request or subsequent push promise
 610          * @param t the Throwable that caused the error
 611          */
 612         void onError(HttpRequest request, Throwable t);
 613 
 614         /**
 615          * Returns a {@link java.util.concurrent.CompletableFuture}{@code <U>}
 616          * which completes when the aggregate result object itself is available.
 617          * It is expected that the returned {@code CompletableFuture} will depend
 618          * on one of the given {@code CompletableFuture<Void}s which themselves complete
 619          * after all individual responses associated with the multi response
 620          * have completed, or after all push promises have been received.
 621          * <p>
 622          * @implNote Implementations might follow the pattern shown below
 623          * <pre>
 624          * {@code
 625          *      CompletableFuture<U> completion(
 626          *              CompletableFuture<Void> onComplete,
 627          *              CompletableFuture<Void> onFinalPushPromise)
 628          *      {
 629          *          return onComplete.thenApply((v) -> {


 700          * {@code
 701          *          HttpRequest request = HttpRequest.newBuilder()
 702          *                  .uri(URI.create("https://www.foo.com/"))
 703          *                  .GET()
 704          *                  .build();
 705          *
 706          *          HttpClient client = HttpClient.newHttpClient();
 707          *
 708          *          Map<HttpRequest,CompletableFuture<HttpResponse<String>>> results = client
 709          *              .sendAsync(request, MultiProcessor.asMap(
 710          *                  (req) -> Optional.of(HttpResponse.BodyHandler.asString())))
 711          *              .join();
 712          * }</pre>
 713          * <p>
 714          * The lambda in this example is the simplest possible implementation,
 715          * where neither the incoming requests are examined, nor the response
 716          * headers, and every push that the server sends is accepted. When the
 717          * join() call returns, all {@code HttpResponse}s and their associated
 718          * body objects are available.
 719          *
 720          * @param <V> the body type used for all responses
 721          * @param pushHandler a function invoked for each request or push
 722          * promise
 723          * @return a MultiProcessor
 724          */
 725         public static <V> MultiProcessor<MultiMapResult<V>,V> asMap(
 726             Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler) {
 727 
 728             return asMap(pushHandler, true);
 729         }
 730 
 731     }
 732 }
< prev index next >