44 import java.util.Objects; |
44 import java.util.Objects; |
45 import java.util.Optional; |
45 import java.util.Optional; |
46 import java.util.concurrent.CompletableFuture; |
46 import java.util.concurrent.CompletableFuture; |
47 import java.util.concurrent.CompletionStage; |
47 import java.util.concurrent.CompletionStage; |
48 import java.util.concurrent.Flow; |
48 import java.util.concurrent.Flow; |
|
49 import java.util.concurrent.Flow.Subscriber; |
49 import java.util.function.Consumer; |
50 import java.util.function.Consumer; |
50 import java.util.function.Function; |
51 import java.util.function.Function; |
51 import javax.net.ssl.SSLParameters; |
52 import javax.net.ssl.SSLParameters; |
52 |
53 |
53 /** |
54 /** |
327 * @return a body subscriber |
328 * @return a body subscriber |
328 */ |
329 */ |
329 public BodySubscriber<T> apply(int statusCode, HttpHeaders responseHeaders); |
330 public BodySubscriber<T> apply(int statusCode, HttpHeaders responseHeaders); |
330 |
331 |
331 /** |
332 /** |
|
333 * Returns a response body handler that returns a {@link BodySubscriber |
|
334 * BodySubscriber}{@code <Void>} obtained from {@linkplain |
|
335 * BodySubscriber#fromSubscriber(Subscriber)}, with the given |
|
336 * {@code subscriber}. |
|
337 * |
|
338 * <p> The response body is not available through this, or the {@code |
|
339 * HttpResponse} API, but instead all response body is forwarded to the |
|
340 * given {@code subscriber}, which should make it available, if |
|
341 * appropriate, through some other mechanism, e.g. an entry in a |
|
342 * database, etc. |
|
343 * |
|
344 * @apiNote This method can be used as an adapter between {@code |
|
345 * BodySubscriber} and {@code Flow.Subscriber}. |
|
346 * |
|
347 * <p> For example: |
|
348 * <pre> {@code |
|
349 * TextSubscriber subscriber = new TextSubscriber(); |
|
350 * HttpResponse<Void> response = client.sendAsync(request, |
|
351 * BodyHandler.fromSubscriber(subscriber)).join(); |
|
352 * System.out.println(response.statusCode()); |
|
353 * }</pre> |
|
354 * |
|
355 * @param subscriber the subscriber |
|
356 * @return a response body handler |
|
357 */ |
|
358 public static BodyHandler<Void> |
|
359 fromSubscriber(Subscriber<? super List<ByteBuffer>> subscriber) { |
|
360 Objects.requireNonNull(subscriber); |
|
361 return (status, headers) -> BodySubscriber.fromSubscriber(subscriber, |
|
362 s -> null); |
|
363 } |
|
364 |
|
365 /** |
|
366 * Returns a response body handler that returns a {@link BodySubscriber |
|
367 * BodySubscriber}{@code <T>} obtained from {@link |
|
368 * BodySubscriber#fromSubscriber(Subscriber, Function)}, with the |
|
369 * given {@code subscriber} and {@code finisher} function. |
|
370 * |
|
371 * <p> The given {@code finisher} function is applied after the given |
|
372 * subscriber's {@code onComplete} has been invoked. The {@code finisher} |
|
373 * function is invoked with the given subscriber, and returns a value |
|
374 * that is set as the response's body. |
|
375 * |
|
376 * @apiNote This method can be used as an adapter between {@code |
|
377 * BodySubscriber} and {@code Flow.Subscriber}. |
|
378 * |
|
379 * <p> For example: |
|
380 * <pre> {@code |
|
381 * TextSubscriber subscriber = ...; // accumulates bytes and transforms them into a String |
|
382 * HttpResponse<String> response = client.sendAsync(request, |
|
383 * BodyHandler.fromSubscriber(subscriber, TextSubscriber::getTextResult)).join(); |
|
384 * String text = response.body(); |
|
385 * }</pre> |
|
386 * |
|
387 * @param <S> the type of the Subscriber |
|
388 * @param <T> the type of the response body |
|
389 * @param subscriber the subscriber |
|
390 * @param finisher a function to be applied after the subscriber has completed |
|
391 * @return a response body handler |
|
392 */ |
|
393 public static <S extends Subscriber<? super List<ByteBuffer>>,T> BodyHandler<T> |
|
394 fromSubscriber(S subscriber, Function<S,T> finisher) { |
|
395 Objects.requireNonNull(subscriber); |
|
396 Objects.requireNonNull(finisher); |
|
397 return (status, headers) -> BodySubscriber.fromSubscriber(subscriber, |
|
398 finisher); |
|
399 } |
|
400 |
|
401 /** |
332 * Returns a response body handler which discards the response body and |
402 * Returns a response body handler which discards the response body and |
333 * uses the given value as a replacement for it. |
403 * uses the given value as a replacement for it. |
334 * |
404 * |
335 * @param <U> the response body type |
405 * @param <U> the response body type |
336 * @param value the value of U to return as the body, may be {@code null} |
406 * @param value the value of U to return as the body, may be {@code null} |
342 |
412 |
343 /** |
413 /** |
344 * Returns a {@code BodyHandler<String>} that returns a |
414 * Returns a {@code BodyHandler<String>} that returns a |
345 * {@link BodySubscriber BodySubscriber}{@code <String>} obtained from |
415 * {@link BodySubscriber BodySubscriber}{@code <String>} obtained from |
346 * {@link BodySubscriber#asString(Charset) BodySubscriber.asString(Charset)}. |
416 * {@link BodySubscriber#asString(Charset) BodySubscriber.asString(Charset)}. |
347 * If a charset is provided, the body is decoded using it. If charset is |
417 * The body is decoded using the given character set. |
348 * {@code null} then the handler tries to determine the character set |
418 * |
349 * from the {@code Content-encoding} header. If that charset is not |
419 * @param charset the character set to convert the body with |
350 * supported then {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} |
|
351 * is used. |
|
352 * |
|
353 * @param charset The name of the charset to interpret the body as. If |
|
354 * {@code null} then the charset is determined from the |
|
355 * <i>Content-encoding</i> header. |
|
356 * @return a response body handler |
420 * @return a response body handler |
357 */ |
421 */ |
358 public static BodyHandler<String> asString(Charset charset) { |
422 public static BodyHandler<String> asString(Charset charset) { |
359 return (status, headers) -> { |
423 Objects.requireNonNull(charset); |
360 if (charset != null) { |
424 return (status, headers) -> BodySubscriber.asString(charset); |
361 return BodySubscriber.asString(charset); |
|
362 } |
|
363 return BodySubscriber.asString(charsetFrom(headers)); |
|
364 }; |
|
365 } |
425 } |
366 |
426 |
367 /** |
427 /** |
368 * Returns a {@code BodyHandler<Path>} that returns a |
428 * Returns a {@code BodyHandler<Path>} that returns a |
369 * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from |
429 * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from |
384 * invoked to check delete access if the file is opened with |
444 * invoked to check delete access if the file is opened with |
385 * the {@code DELETE_ON_CLOSE} option. |
445 * the {@code DELETE_ON_CLOSE} option. |
386 */ |
446 */ |
387 public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) { |
447 public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) { |
388 Objects.requireNonNull(file); |
448 Objects.requireNonNull(file); |
|
449 List<OpenOption> opts = List.of(openOptions); |
389 SecurityManager sm = System.getSecurityManager(); |
450 SecurityManager sm = System.getSecurityManager(); |
390 if (sm != null) { |
451 if (sm != null) { |
391 String fn = pathForSecurityCheck(file); |
452 String fn = pathForSecurityCheck(file); |
392 sm.checkWrite(fn); |
453 sm.checkWrite(fn); |
393 List<OpenOption> opts = Arrays.asList(openOptions); |
|
394 if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) |
454 if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) |
395 sm.checkDelete(fn); |
455 sm.checkDelete(fn); |
396 if (opts.contains(StandardOpenOption.READ)) |
456 if (opts.contains(StandardOpenOption.READ)) |
397 sm.checkRead(fn); |
457 sm.checkRead(fn); |
398 } |
458 } |
448 */ |
508 */ |
449 //####: check if the dir exists and is writable?? |
509 //####: check if the dir exists and is writable?? |
450 public static BodyHandler<Path> asFileDownload(Path directory, |
510 public static BodyHandler<Path> asFileDownload(Path directory, |
451 OpenOption... openOptions) { |
511 OpenOption... openOptions) { |
452 Objects.requireNonNull(directory); |
512 Objects.requireNonNull(directory); |
|
513 List<OpenOption> opts = List.of(openOptions); |
453 SecurityManager sm = System.getSecurityManager(); |
514 SecurityManager sm = System.getSecurityManager(); |
454 if (sm != null) { |
515 if (sm != null) { |
455 String fn = pathForSecurityCheck(directory); |
516 String fn = pathForSecurityCheck(directory); |
456 sm.checkWrite(fn); |
517 sm.checkWrite(fn); |
457 List<OpenOption> opts = Arrays.asList(openOptions); |
|
458 if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) |
518 if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) |
459 sm.checkDelete(fn); |
519 sm.checkDelete(fn); |
460 if (opts.contains(StandardOpenOption.READ)) |
520 if (opts.contains(StandardOpenOption.READ)) |
461 sm.checkRead(fn); |
521 sm.checkRead(fn); |
462 } |
522 } |
545 * @return a body handler |
606 * @return a body handler |
546 * @throws IllegalArgumentException if {@code bufferSize <= 0} |
607 * @throws IllegalArgumentException if {@code bufferSize <= 0} |
547 */ |
608 */ |
548 public static <T> BodyHandler<T> buffering(BodyHandler<T> downstreamHandler, |
609 public static <T> BodyHandler<T> buffering(BodyHandler<T> downstreamHandler, |
549 int bufferSize) { |
610 int bufferSize) { |
|
611 Objects.requireNonNull(downstreamHandler); |
550 if (bufferSize <= 0) |
612 if (bufferSize <= 0) |
551 throw new IllegalArgumentException("must be greater than 0"); |
613 throw new IllegalArgumentException("must be greater than 0"); |
552 return (status, headers) -> BodySubscriber |
614 return (status, headers) -> BodySubscriber |
553 .buffering(downstreamHandler.apply(status, headers), |
615 .buffering(downstreamHandler.apply(status, headers), |
554 bufferSize); |
616 bufferSize); |
601 * @return a CompletionStage for the response body |
663 * @return a CompletionStage for the response body |
602 */ |
664 */ |
603 public CompletionStage<T> getBody(); |
665 public CompletionStage<T> getBody(); |
604 |
666 |
605 /** |
667 /** |
|
668 * Returns a body subscriber that forwards all response body to the |
|
669 * given {@code Flow.Subscriber}. The {@linkplain #getBody()} completion |
|
670 * stage} of the returned body subscriber completes after one of the |
|
671 * given subscribers {@code onComplete} or {@code onError} has been |
|
672 * invoked. |
|
673 * |
|
674 * @apiNote This method can be used as an adapter between {@code |
|
675 * BodySubscriber} and {@code Flow.Subscriber}. |
|
676 * |
|
677 * @param <S> the type of the Subscriber |
|
678 * @param subscriber the subscriber |
|
679 * @return a body subscriber |
|
680 */ |
|
681 public static <S extends Subscriber<? super List<ByteBuffer>>> BodySubscriber<Void> |
|
682 fromSubscriber(S subscriber) { |
|
683 return new ResponseSubscribers.SubscriberAdapter<S,Void>(subscriber, s -> null); |
|
684 } |
|
685 |
|
686 /** |
|
687 * Returns a body subscriber that forwards all response body to the |
|
688 * given {@code Flow.Subscriber}. The {@linkplain #getBody()} completion |
|
689 * stage} of the returned body subscriber completes after one of the |
|
690 * given subscribers {@code onComplete} or {@code onError} has been |
|
691 * invoked. |
|
692 * |
|
693 * <p> The given {@code finisher} function is applied after the given |
|
694 * subscriber's {@code onComplete} has been invoked. The {@code finisher} |
|
695 * function is invoked with the given subscriber, and returns a value |
|
696 * that is set as the response's body. |
|
697 * |
|
698 * @apiNote This method can be used as an adapter between {@code |
|
699 * BodySubscriber} and {@code Flow.Subscriber}. |
|
700 * |
|
701 * @param <S> the type of the Subscriber |
|
702 * @param <T> the type of the response body |
|
703 * @param subscriber the subscriber |
|
704 * @param finisher a function to be applied after the subscriber has |
|
705 * completed |
|
706 * @return a body subscriber |
|
707 */ |
|
708 public static <S extends Subscriber<? super List<ByteBuffer>>,T> BodySubscriber<T> |
|
709 fromSubscriber(S subscriber, |
|
710 Function<S,T> finisher) { |
|
711 return new ResponseSubscribers.SubscriberAdapter<S,T>(subscriber, finisher); |
|
712 } |
|
713 |
|
714 /** |
606 * Returns a body subscriber which stores the response body as a {@code |
715 * Returns a body subscriber which stores the response body as a {@code |
607 * String} converted using the given {@code Charset}. |
716 * String} converted using the given {@code Charset}. |
608 * |
717 * |
609 * <p> The {@link HttpResponse} using this subscriber is available after |
718 * <p> The {@link HttpResponse} using this subscriber is available after |
610 * the entire response has been read. |
719 * the entire response has been read. |
611 * |
720 * |
612 * @param charset the character set to convert the String with |
721 * @param charset the character set to convert the String with |
613 * @return a body subscriber |
722 * @return a body subscriber |
614 */ |
723 */ |
615 public static BodySubscriber<String> asString(Charset charset) { |
724 public static BodySubscriber<String> asString(Charset charset) { |
|
725 Objects.requireNonNull(charset); |
616 return new ResponseSubscribers.ByteArraySubscriber<>( |
726 return new ResponseSubscribers.ByteArraySubscriber<>( |
617 bytes -> new String(bytes, charset) |
727 bytes -> new String(bytes, charset) |
618 ); |
728 ); |
619 } |
729 } |
620 |
730 |
660 * invoked to check delete access if the file is opened with the |
770 * invoked to check delete access if the file is opened with the |
661 * {@code DELETE_ON_CLOSE} option. |
771 * {@code DELETE_ON_CLOSE} option. |
662 */ |
772 */ |
663 public static BodySubscriber<Path> asFile(Path file, OpenOption... openOptions) { |
773 public static BodySubscriber<Path> asFile(Path file, OpenOption... openOptions) { |
664 Objects.requireNonNull(file); |
774 Objects.requireNonNull(file); |
|
775 List<OpenOption> opts = List.of(openOptions); |
665 SecurityManager sm = System.getSecurityManager(); |
776 SecurityManager sm = System.getSecurityManager(); |
666 if (sm != null) { |
777 if (sm != null) { |
667 String fn = pathForSecurityCheck(file); |
778 String fn = pathForSecurityCheck(file); |
668 sm.checkWrite(fn); |
779 sm.checkWrite(fn); |
669 List<OpenOption> opts = Arrays.asList(openOptions); |
|
670 if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) |
780 if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) |
671 sm.checkDelete(fn); |
781 sm.checkDelete(fn); |
672 if (opts.contains(StandardOpenOption.READ)) |
782 if (opts.contains(StandardOpenOption.READ)) |
673 sm.checkRead(fn); |
783 sm.checkRead(fn); |
674 } |
784 } |